Merge 3b20229fa1
into bd4e943597
This commit is contained in:
commit
ef9d389234
41 changed files with 1242 additions and 54 deletions
0
..gitignore.swp
Normal file
0
..gitignore.swp
Normal file
96
Cargo.lock
generated
96
Cargo.lock
generated
|
@ -228,6 +228,7 @@ dependencies = [
|
||||||
"ctor",
|
"ctor",
|
||||||
"db",
|
"db",
|
||||||
"editor",
|
"editor",
|
||||||
|
"encoding",
|
||||||
"env_logger 0.11.8",
|
"env_logger 0.11.8",
|
||||||
"fs",
|
"fs",
|
||||||
"futures 0.3.31",
|
"futures 0.3.31",
|
||||||
|
@ -961,6 +962,7 @@ dependencies = [
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"diffy",
|
"diffy",
|
||||||
"editor",
|
"editor",
|
||||||
|
"encoding",
|
||||||
"feature_flags",
|
"feature_flags",
|
||||||
"fs",
|
"fs",
|
||||||
"futures 0.3.31",
|
"futures 0.3.31",
|
||||||
|
@ -3302,6 +3304,7 @@ dependencies = [
|
||||||
"dashmap 6.1.0",
|
"dashmap 6.1.0",
|
||||||
"debugger_ui",
|
"debugger_ui",
|
||||||
"editor",
|
"editor",
|
||||||
|
"encoding",
|
||||||
"envy",
|
"envy",
|
||||||
"extension",
|
"extension",
|
||||||
"file_finder",
|
"file_finder",
|
||||||
|
@ -3643,6 +3646,7 @@ dependencies = [
|
||||||
"dirs 4.0.0",
|
"dirs 4.0.0",
|
||||||
"edit_prediction",
|
"edit_prediction",
|
||||||
"editor",
|
"editor",
|
||||||
|
"encoding",
|
||||||
"fs",
|
"fs",
|
||||||
"futures 0.3.31",
|
"futures 0.3.31",
|
||||||
"gpui",
|
"gpui",
|
||||||
|
@ -5194,6 +5198,70 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encoding"
|
||||||
|
version = "0.2.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec"
|
||||||
|
dependencies = [
|
||||||
|
"encoding-index-japanese",
|
||||||
|
"encoding-index-korean",
|
||||||
|
"encoding-index-simpchinese",
|
||||||
|
"encoding-index-singlebyte",
|
||||||
|
"encoding-index-tradchinese",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encoding-index-japanese"
|
||||||
|
version = "1.20141219.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91"
|
||||||
|
dependencies = [
|
||||||
|
"encoding_index_tests",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encoding-index-korean"
|
||||||
|
version = "1.20141219.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81"
|
||||||
|
dependencies = [
|
||||||
|
"encoding_index_tests",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encoding-index-simpchinese"
|
||||||
|
version = "1.20141219.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7"
|
||||||
|
dependencies = [
|
||||||
|
"encoding_index_tests",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encoding-index-singlebyte"
|
||||||
|
version = "1.20141219.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a"
|
||||||
|
dependencies = [
|
||||||
|
"encoding_index_tests",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encoding-index-tradchinese"
|
||||||
|
version = "1.20141219.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18"
|
||||||
|
dependencies = [
|
||||||
|
"encoding_index_tests",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encoding_index_tests"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.35"
|
version = "0.8.35"
|
||||||
|
@ -5203,6 +5271,23 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encodings"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"editor",
|
||||||
|
"encoding",
|
||||||
|
"fuzzy",
|
||||||
|
"gpui",
|
||||||
|
"language",
|
||||||
|
"picker",
|
||||||
|
"settings",
|
||||||
|
"ui",
|
||||||
|
"util",
|
||||||
|
"workspace",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "endi"
|
name = "endi"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -5546,6 +5631,7 @@ dependencies = [
|
||||||
"criterion",
|
"criterion",
|
||||||
"ctor",
|
"ctor",
|
||||||
"dap",
|
"dap",
|
||||||
|
"encoding",
|
||||||
"extension",
|
"extension",
|
||||||
"fs",
|
"fs",
|
||||||
"futures 0.3.31",
|
"futures 0.3.31",
|
||||||
|
@ -6042,6 +6128,7 @@ dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"cocoa 0.26.0",
|
"cocoa 0.26.0",
|
||||||
"collections",
|
"collections",
|
||||||
|
"encoding",
|
||||||
"fsevent",
|
"fsevent",
|
||||||
"futures 0.3.31",
|
"futures 0.3.31",
|
||||||
"git",
|
"git",
|
||||||
|
@ -6055,6 +6142,7 @@ dependencies = [
|
||||||
"paths",
|
"paths",
|
||||||
"proto",
|
"proto",
|
||||||
"rope",
|
"rope",
|
||||||
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"smol",
|
"smol",
|
||||||
|
@ -6480,6 +6568,7 @@ dependencies = [
|
||||||
"ctor",
|
"ctor",
|
||||||
"db",
|
"db",
|
||||||
"editor",
|
"editor",
|
||||||
|
"encoding",
|
||||||
"futures 0.3.31",
|
"futures 0.3.31",
|
||||||
"fuzzy",
|
"fuzzy",
|
||||||
"git",
|
"git",
|
||||||
|
@ -9022,6 +9111,7 @@ dependencies = [
|
||||||
"ctor",
|
"ctor",
|
||||||
"diffy",
|
"diffy",
|
||||||
"ec4rs",
|
"ec4rs",
|
||||||
|
"encoding",
|
||||||
"fs",
|
"fs",
|
||||||
"futures 0.3.31",
|
"futures 0.3.31",
|
||||||
"fuzzy",
|
"fuzzy",
|
||||||
|
@ -12562,6 +12652,7 @@ dependencies = [
|
||||||
"context_server",
|
"context_server",
|
||||||
"dap",
|
"dap",
|
||||||
"dap_adapters",
|
"dap_adapters",
|
||||||
|
"encoding",
|
||||||
"extension",
|
"extension",
|
||||||
"fancy-regex 0.14.0",
|
"fancy-regex 0.14.0",
|
||||||
"fs",
|
"fs",
|
||||||
|
@ -13485,6 +13576,7 @@ dependencies = [
|
||||||
"dap_adapters",
|
"dap_adapters",
|
||||||
"debug_adapter_extension",
|
"debug_adapter_extension",
|
||||||
"editor",
|
"editor",
|
||||||
|
"encoding",
|
||||||
"env_logger 0.11.8",
|
"env_logger 0.11.8",
|
||||||
"extension",
|
"extension",
|
||||||
"extension_host",
|
"extension_host",
|
||||||
|
@ -19796,6 +19888,7 @@ dependencies = [
|
||||||
"component",
|
"component",
|
||||||
"dap",
|
"dap",
|
||||||
"db",
|
"db",
|
||||||
|
"encoding",
|
||||||
"fs",
|
"fs",
|
||||||
"futures 0.3.31",
|
"futures 0.3.31",
|
||||||
"gpui",
|
"gpui",
|
||||||
|
@ -20026,6 +20119,7 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clock",
|
"clock",
|
||||||
"collections",
|
"collections",
|
||||||
|
"encoding",
|
||||||
"fs",
|
"fs",
|
||||||
"futures 0.3.31",
|
"futures 0.3.31",
|
||||||
"fuzzy",
|
"fuzzy",
|
||||||
|
@ -20437,6 +20531,8 @@ dependencies = [
|
||||||
"diagnostics",
|
"diagnostics",
|
||||||
"edit_prediction_button",
|
"edit_prediction_button",
|
||||||
"editor",
|
"editor",
|
||||||
|
"encoding",
|
||||||
|
"encodings",
|
||||||
"env_logger 0.11.8",
|
"env_logger 0.11.8",
|
||||||
"extension",
|
"extension",
|
||||||
"extension_host",
|
"extension_host",
|
||||||
|
|
21
Cargo.toml
21
Cargo.toml
|
@ -55,6 +55,7 @@ members = [
|
||||||
"crates/diagnostics",
|
"crates/diagnostics",
|
||||||
"crates/docs_preprocessor",
|
"crates/docs_preprocessor",
|
||||||
"crates/editor",
|
"crates/editor",
|
||||||
|
"crates/encodings",
|
||||||
"crates/eval",
|
"crates/eval",
|
||||||
"crates/explorer_command_injector",
|
"crates/explorer_command_injector",
|
||||||
"crates/extension",
|
"crates/extension",
|
||||||
|
@ -214,7 +215,7 @@ members = [
|
||||||
#
|
#
|
||||||
|
|
||||||
"tooling/workspace-hack",
|
"tooling/workspace-hack",
|
||||||
"tooling/xtask",
|
"tooling/xtask", "crates/encodings",
|
||||||
]
|
]
|
||||||
default-members = ["crates/zed"]
|
default-members = ["crates/zed"]
|
||||||
|
|
||||||
|
@ -237,7 +238,6 @@ activity_indicator = { path = "crates/activity_indicator" }
|
||||||
agent_ui = { path = "crates/agent_ui" }
|
agent_ui = { path = "crates/agent_ui" }
|
||||||
agent_settings = { path = "crates/agent_settings" }
|
agent_settings = { path = "crates/agent_settings" }
|
||||||
agent_servers = { path = "crates/agent_servers" }
|
agent_servers = { path = "crates/agent_servers" }
|
||||||
ai = { path = "crates/ai" }
|
|
||||||
ai_onboarding = { path = "crates/ai_onboarding" }
|
ai_onboarding = { path = "crates/ai_onboarding" }
|
||||||
anthropic = { path = "crates/anthropic" }
|
anthropic = { path = "crates/anthropic" }
|
||||||
askpass = { path = "crates/askpass" }
|
askpass = { path = "crates/askpass" }
|
||||||
|
@ -249,7 +249,6 @@ assistant_tool = { path = "crates/assistant_tool" }
|
||||||
assistant_tools = { path = "crates/assistant_tools" }
|
assistant_tools = { path = "crates/assistant_tools" }
|
||||||
audio = { path = "crates/audio" }
|
audio = { path = "crates/audio" }
|
||||||
auto_update = { path = "crates/auto_update" }
|
auto_update = { path = "crates/auto_update" }
|
||||||
auto_update_helper = { path = "crates/auto_update_helper" }
|
|
||||||
auto_update_ui = { path = "crates/auto_update_ui" }
|
auto_update_ui = { path = "crates/auto_update_ui" }
|
||||||
aws_http_client = { path = "crates/aws_http_client" }
|
aws_http_client = { path = "crates/aws_http_client" }
|
||||||
bedrock = { path = "crates/bedrock" }
|
bedrock = { path = "crates/bedrock" }
|
||||||
|
@ -263,7 +262,6 @@ clock = { path = "crates/clock" }
|
||||||
cloud_api_client = { path = "crates/cloud_api_client" }
|
cloud_api_client = { path = "crates/cloud_api_client" }
|
||||||
cloud_api_types = { path = "crates/cloud_api_types" }
|
cloud_api_types = { path = "crates/cloud_api_types" }
|
||||||
cloud_llm_client = { path = "crates/cloud_llm_client" }
|
cloud_llm_client = { path = "crates/cloud_llm_client" }
|
||||||
collab = { path = "crates/collab" }
|
|
||||||
collab_ui = { path = "crates/collab_ui" }
|
collab_ui = { path = "crates/collab_ui" }
|
||||||
collections = { path = "crates/collections" }
|
collections = { path = "crates/collections" }
|
||||||
command_palette = { path = "crates/command_palette" }
|
command_palette = { path = "crates/command_palette" }
|
||||||
|
@ -309,6 +307,7 @@ icons = { path = "crates/icons" }
|
||||||
image_viewer = { path = "crates/image_viewer" }
|
image_viewer = { path = "crates/image_viewer" }
|
||||||
edit_prediction = { path = "crates/edit_prediction" }
|
edit_prediction = { path = "crates/edit_prediction" }
|
||||||
edit_prediction_button = { path = "crates/edit_prediction_button" }
|
edit_prediction_button = { path = "crates/edit_prediction_button" }
|
||||||
|
encodings = {path = "crates/encodings"}
|
||||||
inspector_ui = { path = "crates/inspector_ui" }
|
inspector_ui = { path = "crates/inspector_ui" }
|
||||||
install_cli = { path = "crates/install_cli" }
|
install_cli = { path = "crates/install_cli" }
|
||||||
jj = { path = "crates/jj" }
|
jj = { path = "crates/jj" }
|
||||||
|
@ -346,8 +345,6 @@ outline_panel = { path = "crates/outline_panel" }
|
||||||
panel = { path = "crates/panel" }
|
panel = { path = "crates/panel" }
|
||||||
paths = { path = "crates/paths" }
|
paths = { path = "crates/paths" }
|
||||||
picker = { path = "crates/picker" }
|
picker = { path = "crates/picker" }
|
||||||
plugin = { path = "crates/plugin" }
|
|
||||||
plugin_macros = { path = "crates/plugin_macros" }
|
|
||||||
prettier = { path = "crates/prettier" }
|
prettier = { path = "crates/prettier" }
|
||||||
settings_profile_selector = { path = "crates/settings_profile_selector" }
|
settings_profile_selector = { path = "crates/settings_profile_selector" }
|
||||||
project = { path = "crates/project" }
|
project = { path = "crates/project" }
|
||||||
|
@ -368,7 +365,6 @@ rope = { path = "crates/rope" }
|
||||||
rpc = { path = "crates/rpc" }
|
rpc = { path = "crates/rpc" }
|
||||||
rules_library = { path = "crates/rules_library" }
|
rules_library = { path = "crates/rules_library" }
|
||||||
search = { path = "crates/search" }
|
search = { path = "crates/search" }
|
||||||
semantic_index = { path = "crates/semantic_index" }
|
|
||||||
semantic_version = { path = "crates/semantic_version" }
|
semantic_version = { path = "crates/semantic_version" }
|
||||||
session = { path = "crates/session" }
|
session = { path = "crates/session" }
|
||||||
settings = { path = "crates/settings" }
|
settings = { path = "crates/settings" }
|
||||||
|
@ -379,7 +375,6 @@ snippets_ui = { path = "crates/snippets_ui" }
|
||||||
sqlez = { path = "crates/sqlez" }
|
sqlez = { path = "crates/sqlez" }
|
||||||
sqlez_macros = { path = "crates/sqlez_macros" }
|
sqlez_macros = { path = "crates/sqlez_macros" }
|
||||||
story = { path = "crates/story" }
|
story = { path = "crates/story" }
|
||||||
storybook = { path = "crates/storybook" }
|
|
||||||
streaming_diff = { path = "crates/streaming_diff" }
|
streaming_diff = { path = "crates/streaming_diff" }
|
||||||
sum_tree = { path = "crates/sum_tree" }
|
sum_tree = { path = "crates/sum_tree" }
|
||||||
supermaven = { path = "crates/supermaven" }
|
supermaven = { path = "crates/supermaven" }
|
||||||
|
@ -395,7 +390,6 @@ terminal_view = { path = "crates/terminal_view" }
|
||||||
text = { path = "crates/text" }
|
text = { path = "crates/text" }
|
||||||
theme = { path = "crates/theme" }
|
theme = { path = "crates/theme" }
|
||||||
theme_extension = { path = "crates/theme_extension" }
|
theme_extension = { path = "crates/theme_extension" }
|
||||||
theme_importer = { path = "crates/theme_importer" }
|
|
||||||
theme_selector = { path = "crates/theme_selector" }
|
theme_selector = { path = "crates/theme_selector" }
|
||||||
time_format = { path = "crates/time_format" }
|
time_format = { path = "crates/time_format" }
|
||||||
title_bar = { path = "crates/title_bar" }
|
title_bar = { path = "crates/title_bar" }
|
||||||
|
@ -467,7 +461,6 @@ ciborium = "0.2"
|
||||||
circular-buffer = "1.0"
|
circular-buffer = "1.0"
|
||||||
clap = { version = "4.4", features = ["derive"] }
|
clap = { version = "4.4", features = ["derive"] }
|
||||||
cocoa = "0.26"
|
cocoa = "0.26"
|
||||||
cocoa-foundation = "0.2.0"
|
|
||||||
convert_case = "0.8.0"
|
convert_case = "0.8.0"
|
||||||
core-foundation = "0.10.0"
|
core-foundation = "0.10.0"
|
||||||
core-foundation-sys = "0.8.6"
|
core-foundation-sys = "0.8.6"
|
||||||
|
@ -484,6 +477,7 @@ documented = "0.9.1"
|
||||||
dotenvy = "0.15.0"
|
dotenvy = "0.15.0"
|
||||||
ec4rs = "1.1"
|
ec4rs = "1.1"
|
||||||
emojis = "0.6.1"
|
emojis = "0.6.1"
|
||||||
|
encoding = "0.2.33"
|
||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
exec = "0.3.1"
|
exec = "0.3.1"
|
||||||
fancy-regex = "0.14.0"
|
fancy-regex = "0.14.0"
|
||||||
|
@ -543,7 +537,6 @@ pet = { git = "https://github.com/microsoft/python-environment-tools.git", rev =
|
||||||
pet-conda = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
|
pet-conda = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
|
||||||
pet-core = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
|
pet-core = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
|
||||||
pet-fs = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
|
pet-fs = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
|
||||||
pet-pixi = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
|
|
||||||
pet-poetry = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
|
pet-poetry = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
|
||||||
pet-reporter = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
|
pet-reporter = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" }
|
||||||
portable-pty = "0.9.0"
|
portable-pty = "0.9.0"
|
||||||
|
@ -666,7 +659,6 @@ wasmtime = { version = "29", default-features = false, features = [
|
||||||
wasmtime-wasi = "29"
|
wasmtime-wasi = "29"
|
||||||
which = "6.0.0"
|
which = "6.0.0"
|
||||||
windows-core = "0.61"
|
windows-core = "0.61"
|
||||||
wit-component = "0.221"
|
|
||||||
workspace-hack = "0.1.0"
|
workspace-hack = "0.1.0"
|
||||||
yawc = "0.2.5"
|
yawc = "0.2.5"
|
||||||
zstd = "0.11"
|
zstd = "0.11"
|
||||||
|
@ -740,11 +732,7 @@ codegen-units = 16
|
||||||
[profile.dev.package]
|
[profile.dev.package]
|
||||||
taffy = { opt-level = 3 }
|
taffy = { opt-level = 3 }
|
||||||
cranelift-codegen = { opt-level = 3 }
|
cranelift-codegen = { opt-level = 3 }
|
||||||
cranelift-codegen-meta = { opt-level = 3 }
|
|
||||||
cranelift-codegen-shared = { opt-level = 3 }
|
|
||||||
resvg = { opt-level = 3 }
|
resvg = { opt-level = 3 }
|
||||||
rustybuzz = { opt-level = 3 }
|
|
||||||
ttf-parser = { opt-level = 3 }
|
|
||||||
wasmtime-cranelift = { opt-level = 3 }
|
wasmtime-cranelift = { opt-level = 3 }
|
||||||
wasmtime = { opt-level = 3 }
|
wasmtime = { opt-level = 3 }
|
||||||
# Build single-source-file crates with cg=1 as it helps make `cargo build` of a whole workspace a bit faster
|
# Build single-source-file crates with cg=1 as it helps make `cargo build` of a whole workspace a bit faster
|
||||||
|
@ -754,7 +742,6 @@ breadcrumbs = { codegen-units = 1 }
|
||||||
collections = { codegen-units = 1 }
|
collections = { codegen-units = 1 }
|
||||||
command_palette = { codegen-units = 1 }
|
command_palette = { codegen-units = 1 }
|
||||||
command_palette_hooks = { codegen-units = 1 }
|
command_palette_hooks = { codegen-units = 1 }
|
||||||
extension_cli = { codegen-units = 1 }
|
|
||||||
feature_flags = { codegen-units = 1 }
|
feature_flags = { codegen-units = 1 }
|
||||||
file_icons = { codegen-units = 1 }
|
file_icons = { codegen-units = 1 }
|
||||||
fsevent = { codegen-units = 1 }
|
fsevent = { codegen-units = 1 }
|
||||||
|
|
|
@ -1258,7 +1258,9 @@
|
||||||
// Whether to show the active language button in the status bar.
|
// Whether to show the active language button in the status bar.
|
||||||
"active_language_button": true,
|
"active_language_button": true,
|
||||||
// Whether to show the cursor position button in the status bar.
|
// Whether to show the cursor position button in the status bar.
|
||||||
"cursor_position_button": true
|
"cursor_position_button": true,
|
||||||
|
// Whether to show the encoding indicator in the status bar.
|
||||||
|
"encoding_indicator": true
|
||||||
},
|
},
|
||||||
// Settings specific to the terminal
|
// Settings specific to the terminal
|
||||||
"terminal": {
|
"terminal": {
|
||||||
|
|
|
@ -32,6 +32,7 @@ cloud_llm_client.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
context_server.workspace = true
|
context_server.workspace = true
|
||||||
db.workspace = true
|
db.workspace = true
|
||||||
|
encoding.workspace = true
|
||||||
fs.workspace = true
|
fs.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
git.workspace = true
|
git.workspace = true
|
||||||
|
@ -72,6 +73,7 @@ which.workspace = true
|
||||||
workspace-hack.workspace = true
|
workspace-hack.workspace = true
|
||||||
zstd.workspace = true
|
zstd.workspace = true
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
agent = { workspace = true, "features" = ["test-support"] }
|
agent = { workspace = true, "features" = ["test-support"] }
|
||||||
agent_servers = { workspace = true, "features" = ["test-support"] }
|
agent_servers = { workspace = true, "features" = ["test-support"] }
|
||||||
|
|
|
@ -523,7 +523,8 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{ContextServerRegistry, Templates};
|
use crate::{ContextServerRegistry, Templates};
|
||||||
use client::TelemetrySettings;
|
use client::TelemetrySettings;
|
||||||
use fs::Fs;
|
use encoding::all::UTF_8;
|
||||||
|
use fs::{Fs, encodings::EncodingWrapper};
|
||||||
use gpui::{TestAppContext, UpdateGlobal};
|
use gpui::{TestAppContext, UpdateGlobal};
|
||||||
use language_model::fake_provider::FakeLanguageModel;
|
use language_model::fake_provider::FakeLanguageModel;
|
||||||
use prompt_store::ProjectContext;
|
use prompt_store::ProjectContext;
|
||||||
|
@ -705,6 +706,7 @@ mod tests {
|
||||||
path!("/root/src/main.rs").as_ref(),
|
path!("/root/src/main.rs").as_ref(),
|
||||||
&"initial content".into(),
|
&"initial content".into(),
|
||||||
language::LineEnding::Unix,
|
language::LineEnding::Unix,
|
||||||
|
EncodingWrapper::new(UTF_8),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -873,6 +875,7 @@ mod tests {
|
||||||
path!("/root/src/main.rs").as_ref(),
|
path!("/root/src/main.rs").as_ref(),
|
||||||
&"initial content".into(),
|
&"initial content".into(),
|
||||||
language::LineEnding::Unix,
|
language::LineEnding::Unix,
|
||||||
|
EncodingWrapper::new(UTF_8),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -28,6 +28,7 @@ component.workspace = true
|
||||||
derive_more.workspace = true
|
derive_more.workspace = true
|
||||||
diffy = "0.4.2"
|
diffy = "0.4.2"
|
||||||
editor.workspace = true
|
editor.workspace = true
|
||||||
|
encoding.workspace = true
|
||||||
feature_flags.workspace = true
|
feature_flags.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
|
|
|
@ -1231,8 +1231,9 @@ async fn build_buffer_diff(
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use ::fs::Fs;
|
use ::fs::{Fs, encodings::EncodingWrapper};
|
||||||
use client::TelemetrySettings;
|
use client::TelemetrySettings;
|
||||||
|
use encoding::all::UTF_8;
|
||||||
use gpui::{TestAppContext, UpdateGlobal};
|
use gpui::{TestAppContext, UpdateGlobal};
|
||||||
use language_model::fake_provider::FakeLanguageModel;
|
use language_model::fake_provider::FakeLanguageModel;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
@ -1501,6 +1502,7 @@ mod tests {
|
||||||
path!("/root/src/main.rs").as_ref(),
|
path!("/root/src/main.rs").as_ref(),
|
||||||
&"initial content".into(),
|
&"initial content".into(),
|
||||||
language::LineEnding::Unix,
|
language::LineEnding::Unix,
|
||||||
|
EncodingWrapper::new(UTF_8),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -1670,6 +1672,7 @@ mod tests {
|
||||||
path!("/root/src/main.rs").as_ref(),
|
path!("/root/src/main.rs").as_ref(),
|
||||||
&"initial content".into(),
|
&"initial content".into(),
|
||||||
language::LineEnding::Unix,
|
language::LineEnding::Unix,
|
||||||
|
EncodingWrapper::new(UTF_8),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -31,6 +31,7 @@ chrono.workspace = true
|
||||||
clock.workspace = true
|
clock.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
dashmap.workspace = true
|
dashmap.workspace = true
|
||||||
|
encoding.workspace = true
|
||||||
envy = "0.4.2"
|
envy = "0.4.2"
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
|
|
|
@ -12,7 +12,8 @@ use buffer_diff::{DiffHunkSecondaryStatus, DiffHunkStatus, assert_hunks};
|
||||||
use call::{ActiveCall, ParticipantLocation, Room, room};
|
use call::{ActiveCall, ParticipantLocation, Room, room};
|
||||||
use client::{RECEIVE_TIMEOUT, User};
|
use client::{RECEIVE_TIMEOUT, User};
|
||||||
use collections::{HashMap, HashSet};
|
use collections::{HashMap, HashSet};
|
||||||
use fs::{FakeFs, Fs as _, RemoveOptions};
|
use encoding::all::UTF_8;
|
||||||
|
use fs::{FakeFs, Fs as _, RemoveOptions, encodings::EncodingWrapper};
|
||||||
use futures::{StreamExt as _, channel::mpsc};
|
use futures::{StreamExt as _, channel::mpsc};
|
||||||
use git::status::{FileStatus, StatusCode, TrackedStatus, UnmergedStatus, UnmergedStatusCode};
|
use git::status::{FileStatus, StatusCode, TrackedStatus, UnmergedStatus, UnmergedStatusCode};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
@ -3706,6 +3707,7 @@ async fn test_buffer_reloading(
|
||||||
path!("/dir/a.txt").as_ref(),
|
path!("/dir/a.txt").as_ref(),
|
||||||
&new_contents,
|
&new_contents,
|
||||||
LineEnding::Windows,
|
LineEnding::Windows,
|
||||||
|
EncodingWrapper::new(UTF_8),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -4472,6 +4474,7 @@ async fn test_reloading_buffer_manually(
|
||||||
path!("/a/a.rs").as_ref(),
|
path!("/a/a.rs").as_ref(),
|
||||||
&Rope::from("let seven = 7;"),
|
&Rope::from("let seven = 7;"),
|
||||||
LineEnding::Unix,
|
LineEnding::Unix,
|
||||||
|
EncodingWrapper::new(UTF_8),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -5,7 +5,8 @@ use async_trait::async_trait;
|
||||||
use call::ActiveCall;
|
use call::ActiveCall;
|
||||||
use collections::{BTreeMap, HashMap};
|
use collections::{BTreeMap, HashMap};
|
||||||
use editor::Bias;
|
use editor::Bias;
|
||||||
use fs::{FakeFs, Fs as _};
|
use encoding::all::UTF_8;
|
||||||
|
use fs::{FakeFs, Fs as _, encodings::EncodingWrapper};
|
||||||
use git::status::{FileStatus, StatusCode, TrackedStatus, UnmergedStatus, UnmergedStatusCode};
|
use git::status::{FileStatus, StatusCode, TrackedStatus, UnmergedStatus, UnmergedStatusCode};
|
||||||
use gpui::{BackgroundExecutor, Entity, TestAppContext};
|
use gpui::{BackgroundExecutor, Entity, TestAppContext};
|
||||||
use language::{
|
use language::{
|
||||||
|
@ -924,7 +925,12 @@ impl RandomizedTest for ProjectCollaborationTest {
|
||||||
|
|
||||||
client
|
client
|
||||||
.fs()
|
.fs()
|
||||||
.save(&path, &content.as_str().into(), text::LineEnding::Unix)
|
.save(
|
||||||
|
&path,
|
||||||
|
&content.as_str().into(),
|
||||||
|
text::LineEnding::Unix,
|
||||||
|
EncodingWrapper::new(UTF_8),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ client.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
command_palette_hooks.workspace = true
|
command_palette_hooks.workspace = true
|
||||||
dirs.workspace = true
|
dirs.workspace = true
|
||||||
|
encoding.workspace = true
|
||||||
fs.workspace = true
|
fs.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
|
@ -54,6 +55,7 @@ workspace.workspace = true
|
||||||
workspace-hack.workspace = true
|
workspace-hack.workspace = true
|
||||||
itertools.workspace = true
|
itertools.workspace = true
|
||||||
|
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
async-std = { version = "1.12.0", features = ["unstable"] }
|
async-std = { version = "1.12.0", features = ["unstable"] }
|
||||||
|
|
||||||
|
|
|
@ -1193,6 +1193,7 @@ async fn get_copilot_lsp(fs: Arc<dyn Fs>, node_runtime: NodeRuntime) -> anyhow::
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use encoding::Encoding;
|
||||||
use gpui::TestAppContext;
|
use gpui::TestAppContext;
|
||||||
use util::path;
|
use util::path;
|
||||||
|
|
||||||
|
@ -1406,6 +1407,10 @@ mod tests {
|
||||||
fn load_bytes(&self, _cx: &App) -> Task<Result<Vec<u8>>> {
|
fn load_bytes(&self, _cx: &App) -> Task<Result<Vec<u8>>> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_with_encoding(&self, _: &App, _: &'static dyn Encoding) -> Task<Result<String>> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -136,6 +136,11 @@ pub struct StatusBar {
|
||||||
///
|
///
|
||||||
/// Default: true
|
/// Default: true
|
||||||
pub cursor_position_button: bool,
|
pub cursor_position_button: bool,
|
||||||
|
|
||||||
|
/// Whether to show the encoding indicator in the status bar.
|
||||||
|
///
|
||||||
|
/// Default: true
|
||||||
|
pub encoding_indicator: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||||
|
@ -593,6 +598,10 @@ pub struct StatusBarContent {
|
||||||
///
|
///
|
||||||
/// Default: true
|
/// Default: true
|
||||||
pub cursor_position_button: Option<bool>,
|
pub cursor_position_button: Option<bool>,
|
||||||
|
/// Whether to show the encoding indicator in the status bar.
|
||||||
|
///
|
||||||
|
/// Default: true
|
||||||
|
pub encoding_indicator: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toolbar related settings
|
// Toolbar related settings
|
||||||
|
|
21
crates/encodings/Cargo.toml
Normal file
21
crates/encodings/Cargo.toml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
[package]
|
||||||
|
name = "encodings"
|
||||||
|
version = "0.1.0"
|
||||||
|
publish.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow.workspace = true
|
||||||
|
editor.workspace = true
|
||||||
|
encoding.workspace = true
|
||||||
|
fuzzy.workspace = true
|
||||||
|
gpui.workspace = true
|
||||||
|
language.workspace = true
|
||||||
|
picker.workspace = true
|
||||||
|
settings.workspace = true
|
||||||
|
ui.workspace = true
|
||||||
|
util.workspace = true
|
||||||
|
workspace.workspace = true
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
244
crates/encodings/src/lib.rs
Normal file
244
crates/encodings/src/lib.rs
Normal file
|
@ -0,0 +1,244 @@
|
||||||
|
///! A crate for handling file encodings in the text editor.
|
||||||
|
use editor::{Editor, EditorSettings};
|
||||||
|
use encoding::Encoding;
|
||||||
|
use encoding::all::{
|
||||||
|
BIG5_2003, EUC_JP, GB18030, GBK, HZ, IBM866, ISO_2022_JP, ISO_8859_1, ISO_8859_2, ISO_8859_3,
|
||||||
|
ISO_8859_4, ISO_8859_5, ISO_8859_6, ISO_8859_7, ISO_8859_8, ISO_8859_10, ISO_8859_13,
|
||||||
|
ISO_8859_14, ISO_8859_15, ISO_8859_16, KOI8_R, KOI8_U, MAC_CYRILLIC, MAC_ROMAN, UTF_8,
|
||||||
|
UTF_16BE, UTF_16LE, WINDOWS_874, WINDOWS_949, WINDOWS_1250, WINDOWS_1251, WINDOWS_1252,
|
||||||
|
WINDOWS_1253, WINDOWS_1254, WINDOWS_1255, WINDOWS_1256, WINDOWS_1257, WINDOWS_1258,
|
||||||
|
};
|
||||||
|
use gpui::{ClickEvent, Entity, Subscription, WeakEntity};
|
||||||
|
use settings::Settings;
|
||||||
|
use ui::{Button, ButtonCommon, Context, LabelSize, Render, Tooltip, Window, div};
|
||||||
|
use ui::{Clickable, ParentElement};
|
||||||
|
use workspace::{ItemHandle, StatusItemView, Workspace};
|
||||||
|
|
||||||
|
use crate::selectors::save_or_reopen::EncodingSaveOrReopenSelector;
|
||||||
|
|
||||||
|
/// A status bar item that shows the current file encoding and allows changing it.
|
||||||
|
pub struct EncodingIndicator {
|
||||||
|
pub encoding: Option<&'static dyn Encoding>,
|
||||||
|
pub workspace: WeakEntity<Workspace>,
|
||||||
|
observe: Option<Subscription>, // Subscription to observe changes in the active editor
|
||||||
|
show: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod selectors;
|
||||||
|
|
||||||
|
impl Render for EncodingIndicator {
|
||||||
|
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl ui::IntoElement {
|
||||||
|
let status_element = div();
|
||||||
|
|
||||||
|
if (EditorSettings::get_global(cx).status_bar.encoding_indicator == false)
|
||||||
|
|| (self.show == false)
|
||||||
|
{
|
||||||
|
return status_element;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_element.child(
|
||||||
|
Button::new("encoding", encoding_name(self.encoding.unwrap_or(UTF_8)))
|
||||||
|
.label_size(LabelSize::Small)
|
||||||
|
.tooltip(Tooltip::text("Select Encoding"))
|
||||||
|
.on_click(cx.listener(|indicator, _: &ClickEvent, window, cx| {
|
||||||
|
if let Some(workspace) = indicator.workspace.upgrade() {
|
||||||
|
workspace.update(cx, |workspace, cx| {
|
||||||
|
EncodingSaveOrReopenSelector::toggle(workspace, window, cx)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EncodingIndicator {
|
||||||
|
pub fn new(
|
||||||
|
encoding: Option<&'static dyn encoding::Encoding>,
|
||||||
|
workspace: WeakEntity<Workspace>,
|
||||||
|
observe: Option<Subscription>,
|
||||||
|
) -> EncodingIndicator {
|
||||||
|
EncodingIndicator {
|
||||||
|
encoding,
|
||||||
|
workspace,
|
||||||
|
observe,
|
||||||
|
show: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(
|
||||||
|
&mut self,
|
||||||
|
editor: Entity<Editor>,
|
||||||
|
_: &mut Window,
|
||||||
|
cx: &mut Context<EncodingIndicator>,
|
||||||
|
) {
|
||||||
|
let editor = editor.read(cx);
|
||||||
|
if let Some((_, buffer, _)) = editor.active_excerpt(cx) {
|
||||||
|
let encoding = buffer.read(cx).encoding;
|
||||||
|
self.encoding = Some(encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StatusItemView for EncodingIndicator {
|
||||||
|
fn set_active_pane_item(
|
||||||
|
&mut self,
|
||||||
|
active_pane_item: Option<&dyn ItemHandle>,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
|
match active_pane_item.and_then(|item| item.downcast::<Editor>()) {
|
||||||
|
Some(editor) => {
|
||||||
|
self.observe = Some(cx.observe_in(&editor, window, Self::update));
|
||||||
|
self.update(editor, window, cx);
|
||||||
|
self.show = true;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.encoding = None;
|
||||||
|
self.observe = None;
|
||||||
|
self.show = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a human-readable name for the given encoding.
|
||||||
|
pub fn encoding_name(encoding: &'static dyn Encoding) -> String {
|
||||||
|
let name = encoding.name();
|
||||||
|
|
||||||
|
match () {
|
||||||
|
() if name == UTF_8.name() => "UTF-8",
|
||||||
|
() if name == UTF_16LE.name() => "UTF-16 LE",
|
||||||
|
() if name == UTF_16BE.name() => "UTF-16 BE",
|
||||||
|
() if name == IBM866.name() => "IBM866",
|
||||||
|
() if name == ISO_8859_1.name() => "ISO 8859-1",
|
||||||
|
() if name == ISO_8859_2.name() => "ISO 8859-2",
|
||||||
|
() if name == ISO_8859_3.name() => "ISO 8859-3",
|
||||||
|
() if name == ISO_8859_4.name() => "ISO 8859-4",
|
||||||
|
() if name == ISO_8859_5.name() => "ISO 8859-5",
|
||||||
|
() if name == ISO_8859_6.name() => "ISO 8859-6",
|
||||||
|
() if name == ISO_8859_7.name() => "ISO 8859-7",
|
||||||
|
() if name == ISO_8859_8.name() => "ISO 8859-8",
|
||||||
|
() if name == ISO_8859_10.name() => "ISO 8859-10",
|
||||||
|
() if name == ISO_8859_13.name() => "ISO 8859-13",
|
||||||
|
() if name == ISO_8859_14.name() => "ISO 8859-14",
|
||||||
|
() if name == ISO_8859_15.name() => "ISO 8859-15",
|
||||||
|
() if name == ISO_8859_16.name() => "ISO 8859-16",
|
||||||
|
() if name == KOI8_R.name() => "KOI8-R",
|
||||||
|
() if name == KOI8_U.name() => "KOI8-U",
|
||||||
|
() if name == MAC_ROMAN.name() => "MacRoman",
|
||||||
|
() if name == MAC_CYRILLIC.name() => "Mac Cyrillic",
|
||||||
|
() if name == WINDOWS_874.name() => "Windows-874",
|
||||||
|
() if name == WINDOWS_1250.name() => "Windows-1250",
|
||||||
|
() if name == WINDOWS_1251.name() => "Windows-1251",
|
||||||
|
() if name == WINDOWS_1252.name() => "Windows-1252",
|
||||||
|
() if name == WINDOWS_1253.name() => "Windows-1253",
|
||||||
|
() if name == WINDOWS_1254.name() => "Windows-1254",
|
||||||
|
() if name == WINDOWS_1255.name() => "Windows-1255",
|
||||||
|
() if name == WINDOWS_1256.name() => "Windows-1256",
|
||||||
|
() if name == WINDOWS_1257.name() => "Windows-1257",
|
||||||
|
() if name == WINDOWS_1258.name() => "Windows-1258",
|
||||||
|
() if name == WINDOWS_949.name() => "Windows-949",
|
||||||
|
() if name == EUC_JP.name() => "EUC-JP",
|
||||||
|
() if name == ISO_2022_JP.name() => "ISO 2022-JP",
|
||||||
|
() if name == GBK.name() => "GBK",
|
||||||
|
() if name == GB18030.name() => "GB18030",
|
||||||
|
() if name == BIG5_2003.name() => "Big5",
|
||||||
|
() if name == HZ.name() => "HZ-GB-2312",
|
||||||
|
_ => "",
|
||||||
|
}
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get an encoding from its index in the predefined list.
|
||||||
|
/// If the index is out of range, UTF-8 is returned as a default.
|
||||||
|
pub fn encoding_from_index(index: usize) -> &'static dyn Encoding {
|
||||||
|
match index {
|
||||||
|
0 => UTF_8,
|
||||||
|
1 => UTF_16LE,
|
||||||
|
2 => UTF_16BE,
|
||||||
|
3 => IBM866,
|
||||||
|
4 => ISO_8859_1,
|
||||||
|
5 => ISO_8859_2,
|
||||||
|
6 => ISO_8859_3,
|
||||||
|
7 => ISO_8859_4,
|
||||||
|
8 => ISO_8859_5,
|
||||||
|
9 => ISO_8859_6,
|
||||||
|
10 => ISO_8859_7,
|
||||||
|
11 => ISO_8859_8,
|
||||||
|
12 => ISO_8859_10,
|
||||||
|
13 => ISO_8859_13,
|
||||||
|
14 => ISO_8859_14,
|
||||||
|
15 => ISO_8859_15,
|
||||||
|
16 => ISO_8859_16,
|
||||||
|
17 => KOI8_R,
|
||||||
|
18 => KOI8_U,
|
||||||
|
19 => MAC_ROMAN,
|
||||||
|
20 => MAC_CYRILLIC,
|
||||||
|
21 => WINDOWS_874,
|
||||||
|
22 => WINDOWS_1250,
|
||||||
|
23 => WINDOWS_1251,
|
||||||
|
24 => WINDOWS_1252,
|
||||||
|
25 => WINDOWS_1253,
|
||||||
|
26 => WINDOWS_1254,
|
||||||
|
27 => WINDOWS_1255,
|
||||||
|
28 => WINDOWS_1256,
|
||||||
|
29 => WINDOWS_1257,
|
||||||
|
30 => WINDOWS_1258,
|
||||||
|
31 => WINDOWS_949,
|
||||||
|
32 => EUC_JP,
|
||||||
|
33 => ISO_2022_JP,
|
||||||
|
34 => GBK,
|
||||||
|
35 => GB18030,
|
||||||
|
36 => BIG5_2003,
|
||||||
|
37 => HZ,
|
||||||
|
_ => UTF_8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get an encoding from its name.
|
||||||
|
pub fn encoding_from_name(name: &str) -> &'static dyn Encoding {
|
||||||
|
match name {
|
||||||
|
"UTF-8" => UTF_8,
|
||||||
|
"UTF-16 LE" => UTF_16LE,
|
||||||
|
"UTF-16 BE" => UTF_16BE,
|
||||||
|
"IBM866" => IBM866,
|
||||||
|
"ISO 8859-1" => ISO_8859_1,
|
||||||
|
"ISO 8859-2" => ISO_8859_2,
|
||||||
|
"ISO 8859-3" => ISO_8859_3,
|
||||||
|
"ISO 8859-4" => ISO_8859_4,
|
||||||
|
"ISO 8859-5" => ISO_8859_5,
|
||||||
|
"ISO 8859-6" => ISO_8859_6,
|
||||||
|
"ISO 8859-7" => ISO_8859_7,
|
||||||
|
"ISO 8859-8" => ISO_8859_8,
|
||||||
|
"ISO 8859-10" => ISO_8859_10,
|
||||||
|
"ISO 8859-13" => ISO_8859_13,
|
||||||
|
"ISO 8859-14" => ISO_8859_14,
|
||||||
|
"ISO 8859-15" => ISO_8859_15,
|
||||||
|
"ISO 8859-16" => ISO_8859_16,
|
||||||
|
"KOI8-R" => KOI8_R,
|
||||||
|
"KOI8-U" => KOI8_U,
|
||||||
|
"MacRoman" => MAC_ROMAN,
|
||||||
|
"Mac Cyrillic" => MAC_CYRILLIC,
|
||||||
|
"Windows-874" => WINDOWS_874,
|
||||||
|
"Windows-1250" => WINDOWS_1250,
|
||||||
|
"Windows-1251" => WINDOWS_1251,
|
||||||
|
"Windows-1252" => WINDOWS_1252,
|
||||||
|
"Windows-1253" => WINDOWS_1253,
|
||||||
|
"Windows-1254" => WINDOWS_1254,
|
||||||
|
"Windows-1255" => WINDOWS_1255,
|
||||||
|
"Windows-1256" => WINDOWS_1256,
|
||||||
|
"Windows-1257" => WINDOWS_1257,
|
||||||
|
"Windows-1258" => WINDOWS_1258,
|
||||||
|
"Windows-949" => WINDOWS_949,
|
||||||
|
"EUC-JP" => EUC_JP,
|
||||||
|
"ISO 2022-JP" => ISO_2022_JP,
|
||||||
|
"GBK" => GBK,
|
||||||
|
"GB18030" => GB18030,
|
||||||
|
"Big5" => BIG5_2003,
|
||||||
|
"HZ-GB-2312" => HZ,
|
||||||
|
_ => UTF_8, // Default to UTF-8 for unknown names
|
||||||
|
}
|
||||||
|
}
|
530
crates/encodings/src/selectors.rs
Normal file
530
crates/encodings/src/selectors.rs
Normal file
|
@ -0,0 +1,530 @@
|
||||||
|
/// This module contains the encoding selectors for saving or reopening files with a different encoding.
|
||||||
|
/// It provides a modal view that allows the user to choose between saving with a different encoding
|
||||||
|
/// or reopening with a different encoding, and then selecting the desired encoding from a list.
|
||||||
|
pub mod save_or_reopen {
|
||||||
|
use editor::Editor;
|
||||||
|
use gpui::Styled;
|
||||||
|
use gpui::{AppContext, ParentElement};
|
||||||
|
use picker::Picker;
|
||||||
|
use picker::PickerDelegate;
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
use util::ResultExt;
|
||||||
|
|
||||||
|
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||||
|
use gpui::{DismissEvent, Entity, EventEmitter, Focusable, WeakEntity};
|
||||||
|
|
||||||
|
use ui::{Context, HighlightedLabel, ListItem, Render, Window, rems, v_flex};
|
||||||
|
use workspace::{ModalView, Workspace};
|
||||||
|
|
||||||
|
use crate::selectors::encoding::{Action, EncodingSelector};
|
||||||
|
|
||||||
|
/// A modal view that allows the user to select between saving with a different encoding or
|
||||||
|
/// reopening with a different encoding.
|
||||||
|
pub struct EncodingSaveOrReopenSelector {
|
||||||
|
picker: Entity<Picker<EncodingSaveOrReopenDelegate>>,
|
||||||
|
pub current_selection: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EncodingSaveOrReopenSelector {
|
||||||
|
pub fn new(
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<EncodingSaveOrReopenSelector>,
|
||||||
|
workspace: WeakEntity<Workspace>,
|
||||||
|
) -> Self {
|
||||||
|
let delegate =
|
||||||
|
EncodingSaveOrReopenDelegate::new(cx.entity().downgrade(), workspace.clone());
|
||||||
|
|
||||||
|
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
|
||||||
|
|
||||||
|
Self {
|
||||||
|
picker,
|
||||||
|
current_selection: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Toggle the modal view for selecting between saving with a different encoding or
|
||||||
|
/// reopening with a different encoding.
|
||||||
|
pub fn toggle(workspace: &mut Workspace, window: &mut Window, cx: &mut Context<Workspace>) {
|
||||||
|
let weak_workspace = workspace.weak_handle();
|
||||||
|
workspace.toggle_modal(window, cx, |window, cx| {
|
||||||
|
EncodingSaveOrReopenSelector::new(window, cx, weak_workspace)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Focusable for EncodingSaveOrReopenSelector {
|
||||||
|
fn focus_handle(&self, cx: &ui::App) -> gpui::FocusHandle {
|
||||||
|
self.picker.focus_handle(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for EncodingSaveOrReopenSelector {
|
||||||
|
fn render(
|
||||||
|
&mut self,
|
||||||
|
_window: &mut Window,
|
||||||
|
_cx: &mut Context<Self>,
|
||||||
|
) -> impl ui::IntoElement {
|
||||||
|
v_flex().w(rems(34.0)).child(self.picker.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModalView for EncodingSaveOrReopenSelector {}
|
||||||
|
|
||||||
|
impl EventEmitter<DismissEvent> for EncodingSaveOrReopenSelector {}
|
||||||
|
|
||||||
|
pub struct EncodingSaveOrReopenDelegate {
|
||||||
|
selector: WeakEntity<EncodingSaveOrReopenSelector>,
|
||||||
|
current_selection: usize,
|
||||||
|
matches: Vec<StringMatch>,
|
||||||
|
pub actions: Vec<StringMatchCandidate>,
|
||||||
|
workspace: WeakEntity<Workspace>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EncodingSaveOrReopenDelegate {
|
||||||
|
pub fn new(
|
||||||
|
selector: WeakEntity<EncodingSaveOrReopenSelector>,
|
||||||
|
workspace: WeakEntity<Workspace>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
selector,
|
||||||
|
current_selection: 0,
|
||||||
|
matches: Vec::new(),
|
||||||
|
actions: vec![
|
||||||
|
StringMatchCandidate::new(0, "Save with encoding"),
|
||||||
|
StringMatchCandidate::new(1, "Reopen with encoding"),
|
||||||
|
],
|
||||||
|
workspace,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_actions(&self) -> (&str, &str) {
|
||||||
|
(&self.actions[0].string, &self.actions[1].string)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle the action selected by the user.
|
||||||
|
pub fn post_selection(
|
||||||
|
&self,
|
||||||
|
cx: &mut Context<Picker<EncodingSaveOrReopenDelegate>>,
|
||||||
|
window: &mut Window,
|
||||||
|
) -> Option<()> {
|
||||||
|
if self.current_selection == 0 {
|
||||||
|
if let Some(workspace) = self.workspace.upgrade() {
|
||||||
|
let (_, buffer, _) = workspace
|
||||||
|
.read(cx)
|
||||||
|
.active_item(cx)?
|
||||||
|
.act_as::<Editor>(cx)?
|
||||||
|
.read(cx)
|
||||||
|
.active_excerpt(cx)?;
|
||||||
|
|
||||||
|
let weak_workspace = workspace.read(cx).weak_handle();
|
||||||
|
|
||||||
|
workspace.update(cx, |workspace, cx| {
|
||||||
|
workspace.toggle_modal(window, cx, |window, cx| {
|
||||||
|
EncodingSelector::new(
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
Action::Save,
|
||||||
|
buffer.downgrade(),
|
||||||
|
weak_workspace,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if self.current_selection == 1 {
|
||||||
|
if let Some(workspace) = self.workspace.upgrade() {
|
||||||
|
let (_, buffer, _) = workspace
|
||||||
|
.read(cx)
|
||||||
|
.active_item(cx)?
|
||||||
|
.act_as::<Editor>(cx)?
|
||||||
|
.read(cx)
|
||||||
|
.active_excerpt(cx)?;
|
||||||
|
|
||||||
|
let weak_workspace = workspace.read(cx).weak_handle();
|
||||||
|
|
||||||
|
workspace.update(cx, |workspace, cx| {
|
||||||
|
workspace.toggle_modal(window, cx, |window, cx| {
|
||||||
|
EncodingSelector::new(
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
Action::Reopen,
|
||||||
|
buffer.downgrade(),
|
||||||
|
weak_workspace,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PickerDelegate for EncodingSaveOrReopenDelegate {
|
||||||
|
type ListItem = ListItem;
|
||||||
|
|
||||||
|
fn match_count(&self) -> usize {
|
||||||
|
self.matches.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn selected_index(&self) -> usize {
|
||||||
|
self.current_selection
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_selected_index(
|
||||||
|
&mut self,
|
||||||
|
ix: usize,
|
||||||
|
_window: &mut Window,
|
||||||
|
cx: &mut Context<Picker<Self>>,
|
||||||
|
) {
|
||||||
|
self.current_selection = ix;
|
||||||
|
self.selector
|
||||||
|
.update(cx, |selector, _cx| {
|
||||||
|
selector.current_selection = ix;
|
||||||
|
})
|
||||||
|
.log_err();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn placeholder_text(&self, _window: &mut Window, _cx: &mut ui::App) -> std::sync::Arc<str> {
|
||||||
|
"Select an action...".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_matches(
|
||||||
|
&mut self,
|
||||||
|
query: String,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Picker<Self>>,
|
||||||
|
) -> gpui::Task<()> {
|
||||||
|
let executor = cx.background_executor().clone();
|
||||||
|
let actions = self.actions.clone();
|
||||||
|
|
||||||
|
cx.spawn_in(window, async move |this, cx| {
|
||||||
|
let matches = if query.is_empty() {
|
||||||
|
actions
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(index, value)| StringMatch {
|
||||||
|
candidate_id: index,
|
||||||
|
score: 0.0,
|
||||||
|
positions: vec![],
|
||||||
|
string: value.string,
|
||||||
|
})
|
||||||
|
.collect::<Vec<StringMatch>>()
|
||||||
|
} else {
|
||||||
|
fuzzy::match_strings(
|
||||||
|
&actions,
|
||||||
|
&query,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
2,
|
||||||
|
&AtomicBool::new(false),
|
||||||
|
executor,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
};
|
||||||
|
|
||||||
|
this.update(cx, |picker, cx| {
|
||||||
|
let delegate = &mut picker.delegate;
|
||||||
|
delegate.matches = matches;
|
||||||
|
delegate.current_selection = delegate
|
||||||
|
.current_selection
|
||||||
|
.min(delegate.matches.len().saturating_sub(1));
|
||||||
|
delegate
|
||||||
|
.selector
|
||||||
|
.update(cx, |selector, _cx| {
|
||||||
|
selector.current_selection = delegate.current_selection
|
||||||
|
})
|
||||||
|
.log_err();
|
||||||
|
cx.notify();
|
||||||
|
})
|
||||||
|
.log_err();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn confirm(&mut self, _: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
|
||||||
|
self.dismissed(window, cx);
|
||||||
|
if self.selector.is_upgradable() {
|
||||||
|
self.post_selection(cx, window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dismissed(&mut self, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
|
||||||
|
self.selector
|
||||||
|
.update(cx, |_, cx| cx.emit(DismissEvent))
|
||||||
|
.log_err();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_match(
|
||||||
|
&self,
|
||||||
|
ix: usize,
|
||||||
|
_: bool,
|
||||||
|
_: &mut Window,
|
||||||
|
_: &mut Context<Picker<Self>>,
|
||||||
|
) -> Option<Self::ListItem> {
|
||||||
|
Some(
|
||||||
|
ListItem::new(ix)
|
||||||
|
.child(HighlightedLabel::new(
|
||||||
|
&self.matches[ix].string,
|
||||||
|
self.matches[ix].positions.clone(),
|
||||||
|
))
|
||||||
|
.spacing(ui::ListItemSpacing::Sparse),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_current_encoding() -> &'static str {
|
||||||
|
"UTF-8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This module contains the encoding selector for choosing an encoding to save or reopen a file with.
|
||||||
|
pub mod encoding {
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
|
||||||
|
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||||
|
use gpui::{AppContext, DismissEvent, Entity, EventEmitter, Focusable, WeakEntity};
|
||||||
|
use language::Buffer;
|
||||||
|
use picker::{Picker, PickerDelegate};
|
||||||
|
use ui::{
|
||||||
|
Context, HighlightedLabel, ListItem, ListItemSpacing, ParentElement, Render, Styled,
|
||||||
|
Window, rems, v_flex,
|
||||||
|
};
|
||||||
|
use util::{ResultExt, TryFutureExt};
|
||||||
|
use workspace::{ModalView, Workspace};
|
||||||
|
|
||||||
|
use crate::encoding_from_name;
|
||||||
|
|
||||||
|
/// A modal view that allows the user to select an encoding from a list of encodings.
|
||||||
|
pub struct EncodingSelector {
|
||||||
|
picker: Entity<Picker<EncodingSelectorDelegate>>,
|
||||||
|
workspace: WeakEntity<Workspace>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EncodingSelectorDelegate {
|
||||||
|
current_selection: usize,
|
||||||
|
encodings: Vec<StringMatchCandidate>,
|
||||||
|
matches: Vec<StringMatch>,
|
||||||
|
selector: WeakEntity<EncodingSelector>,
|
||||||
|
buffer: WeakEntity<Buffer>,
|
||||||
|
action: Action,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EncodingSelectorDelegate {
|
||||||
|
pub fn new(
|
||||||
|
selector: WeakEntity<EncodingSelector>,
|
||||||
|
buffer: WeakEntity<Buffer>,
|
||||||
|
action: Action,
|
||||||
|
) -> EncodingSelectorDelegate {
|
||||||
|
EncodingSelectorDelegate {
|
||||||
|
current_selection: 0,
|
||||||
|
encodings: vec![
|
||||||
|
StringMatchCandidate::new(0, "UTF-8"),
|
||||||
|
StringMatchCandidate::new(1, "UTF-16 LE"),
|
||||||
|
StringMatchCandidate::new(2, "UTF-16 BE"),
|
||||||
|
StringMatchCandidate::new(3, "IBM866"),
|
||||||
|
StringMatchCandidate::new(4, "ISO 8859-1"),
|
||||||
|
StringMatchCandidate::new(5, "ISO 8859-2"),
|
||||||
|
StringMatchCandidate::new(6, "ISO 8859-3"),
|
||||||
|
StringMatchCandidate::new(7, "ISO 8859-4"),
|
||||||
|
StringMatchCandidate::new(8, "ISO 8859-5"),
|
||||||
|
StringMatchCandidate::new(9, "ISO 8859-6"),
|
||||||
|
StringMatchCandidate::new(10, "ISO 8859-7"),
|
||||||
|
StringMatchCandidate::new(11, "ISO 8859-8"),
|
||||||
|
StringMatchCandidate::new(12, "ISO 8859-10"),
|
||||||
|
StringMatchCandidate::new(13, "ISO 8859-13"),
|
||||||
|
StringMatchCandidate::new(14, "ISO 8859-14"),
|
||||||
|
StringMatchCandidate::new(15, "ISO 8859-15"),
|
||||||
|
StringMatchCandidate::new(16, "ISO 8859-16"),
|
||||||
|
StringMatchCandidate::new(17, "KOI8-R"),
|
||||||
|
StringMatchCandidate::new(18, "KOI8-U"),
|
||||||
|
StringMatchCandidate::new(19, "MacRoman"),
|
||||||
|
StringMatchCandidate::new(20, "Mac Cyrillic"),
|
||||||
|
StringMatchCandidate::new(21, "Windows-874"),
|
||||||
|
StringMatchCandidate::new(22, "Windows-1250"),
|
||||||
|
StringMatchCandidate::new(23, "Windows-1251"),
|
||||||
|
StringMatchCandidate::new(24, "Windows-1252"),
|
||||||
|
StringMatchCandidate::new(25, "Windows-1253"),
|
||||||
|
StringMatchCandidate::new(26, "Windows-1254"),
|
||||||
|
StringMatchCandidate::new(27, "Windows-1255"),
|
||||||
|
StringMatchCandidate::new(28, "Windows-1256"),
|
||||||
|
StringMatchCandidate::new(29, "Windows-1257"),
|
||||||
|
StringMatchCandidate::new(30, "Windows-1258"),
|
||||||
|
StringMatchCandidate::new(31, "Windows-949"),
|
||||||
|
StringMatchCandidate::new(32, "EUC-JP"),
|
||||||
|
StringMatchCandidate::new(33, "ISO 2022-JP"),
|
||||||
|
StringMatchCandidate::new(34, "GBK"),
|
||||||
|
StringMatchCandidate::new(35, "GB18030"),
|
||||||
|
StringMatchCandidate::new(36, "Big5"),
|
||||||
|
StringMatchCandidate::new(37, "HZ-GB-2312"),
|
||||||
|
],
|
||||||
|
matches: Vec::new(),
|
||||||
|
selector,
|
||||||
|
buffer,
|
||||||
|
action,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PickerDelegate for EncodingSelectorDelegate {
|
||||||
|
type ListItem = ListItem;
|
||||||
|
|
||||||
|
fn match_count(&self) -> usize {
|
||||||
|
self.matches.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn selected_index(&self) -> usize {
|
||||||
|
self.current_selection
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_selected_index(&mut self, ix: usize, _: &mut Window, _: &mut Context<Picker<Self>>) {
|
||||||
|
self.current_selection = ix;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn placeholder_text(&self, _window: &mut Window, _cx: &mut ui::App) -> std::sync::Arc<str> {
|
||||||
|
"Select an encoding...".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_matches(
|
||||||
|
&mut self,
|
||||||
|
query: String,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Picker<Self>>,
|
||||||
|
) -> gpui::Task<()> {
|
||||||
|
let executor = cx.background_executor().clone();
|
||||||
|
let encodings = self.encodings.clone();
|
||||||
|
|
||||||
|
cx.spawn_in(window, async move |picker, cx| {
|
||||||
|
let matches: Vec<StringMatch>;
|
||||||
|
|
||||||
|
if query.is_empty() {
|
||||||
|
matches = encodings
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(index, value)| StringMatch {
|
||||||
|
candidate_id: index,
|
||||||
|
score: 0.0,
|
||||||
|
positions: Vec::new(),
|
||||||
|
string: value.string,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
} else {
|
||||||
|
matches = fuzzy::match_strings(
|
||||||
|
&encodings,
|
||||||
|
&query,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
38,
|
||||||
|
&AtomicBool::new(false),
|
||||||
|
executor,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
picker
|
||||||
|
.update(cx, |picker, cx| {
|
||||||
|
let delegate = &mut picker.delegate;
|
||||||
|
delegate.matches = matches;
|
||||||
|
delegate.current_selection = delegate
|
||||||
|
.current_selection
|
||||||
|
.min(delegate.matches.len().saturating_sub(1));
|
||||||
|
cx.notify();
|
||||||
|
})
|
||||||
|
.log_err();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn confirm(&mut self, _: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
|
||||||
|
if let Some(buffer) = self.buffer.upgrade() {
|
||||||
|
buffer.update(cx, |buffer, cx| {
|
||||||
|
buffer.encoding =
|
||||||
|
encoding_from_name(self.matches[self.current_selection].string.as_str());
|
||||||
|
if self.action == Action::Reopen {
|
||||||
|
let executor = cx.background_executor().clone();
|
||||||
|
executor.spawn(buffer.reload(cx)).detach();
|
||||||
|
} else if self.action == Action::Save {
|
||||||
|
let executor = cx.background_executor().clone();
|
||||||
|
|
||||||
|
let workspace = self
|
||||||
|
.selector
|
||||||
|
.upgrade()
|
||||||
|
.unwrap()
|
||||||
|
.read(cx)
|
||||||
|
.workspace
|
||||||
|
.upgrade()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
executor
|
||||||
|
.spawn(workspace.update(cx, |workspace, cx| {
|
||||||
|
workspace
|
||||||
|
.save_active_item(workspace::SaveIntent::Save, window, cx)
|
||||||
|
.log_err()
|
||||||
|
}))
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
self.dismissed(window, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dismissed(&mut self, _: &mut Window, cx: &mut Context<Picker<Self>>) {
|
||||||
|
self.selector
|
||||||
|
.update(cx, |_, cx| cx.emit(DismissEvent))
|
||||||
|
.log_err();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_match(
|
||||||
|
&self,
|
||||||
|
ix: usize,
|
||||||
|
_: bool,
|
||||||
|
_: &mut Window,
|
||||||
|
_: &mut Context<Picker<Self>>,
|
||||||
|
) -> Option<Self::ListItem> {
|
||||||
|
Some(
|
||||||
|
ListItem::new(ix)
|
||||||
|
.child(HighlightedLabel::new(
|
||||||
|
&self.matches[ix].string,
|
||||||
|
self.matches[ix].positions.clone(),
|
||||||
|
))
|
||||||
|
.spacing(ListItemSpacing::Sparse),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The action to perform after selecting an encoding.
|
||||||
|
#[derive(PartialEq, Clone)]
|
||||||
|
pub enum Action {
|
||||||
|
Save,
|
||||||
|
Reopen,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EncodingSelector {
|
||||||
|
pub fn new(
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<EncodingSelector>,
|
||||||
|
action: Action,
|
||||||
|
buffer: WeakEntity<Buffer>,
|
||||||
|
workspace: WeakEntity<Workspace>,
|
||||||
|
) -> EncodingSelector {
|
||||||
|
let delegate =
|
||||||
|
EncodingSelectorDelegate::new(cx.entity().downgrade(), buffer, action.clone());
|
||||||
|
let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
|
||||||
|
|
||||||
|
EncodingSelector { picker, workspace }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventEmitter<DismissEvent> for EncodingSelector {}
|
||||||
|
|
||||||
|
impl Focusable for EncodingSelector {
|
||||||
|
fn focus_handle(&self, cx: &ui::App) -> gpui::FocusHandle {
|
||||||
|
cx.focus_handle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModalView for EncodingSelector {}
|
||||||
|
|
||||||
|
impl Render for EncodingSelector {
|
||||||
|
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl ui::IntoElement {
|
||||||
|
v_flex().w(rems(34.0)).child(self.picker.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ async-trait.workspace = true
|
||||||
client.workspace = true
|
client.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
dap.workspace = true
|
dap.workspace = true
|
||||||
|
encoding.workspace = true
|
||||||
extension.workspace = true
|
extension.workspace = true
|
||||||
fs.workspace = true
|
fs.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
|
|
|
@ -12,6 +12,7 @@ use async_tar::Archive;
|
||||||
use client::ExtensionProvides;
|
use client::ExtensionProvides;
|
||||||
use client::{Client, ExtensionMetadata, GetExtensionsResponse, proto, telemetry::Telemetry};
|
use client::{Client, ExtensionMetadata, GetExtensionsResponse, proto, telemetry::Telemetry};
|
||||||
use collections::{BTreeMap, BTreeSet, HashMap, HashSet, btree_map};
|
use collections::{BTreeMap, BTreeSet, HashMap, HashSet, btree_map};
|
||||||
|
use encoding::all::UTF_8;
|
||||||
pub use extension::ExtensionManifest;
|
pub use extension::ExtensionManifest;
|
||||||
use extension::extension_builder::{CompileExtensionOptions, ExtensionBuilder};
|
use extension::extension_builder::{CompileExtensionOptions, ExtensionBuilder};
|
||||||
use extension::{
|
use extension::{
|
||||||
|
@ -20,6 +21,7 @@ use extension::{
|
||||||
ExtensionLanguageServerProxy, ExtensionSlashCommandProxy, ExtensionSnippetProxy,
|
ExtensionLanguageServerProxy, ExtensionSlashCommandProxy, ExtensionSnippetProxy,
|
||||||
ExtensionThemeProxy,
|
ExtensionThemeProxy,
|
||||||
};
|
};
|
||||||
|
use fs::encodings::EncodingWrapper;
|
||||||
use fs::{Fs, RemoveOptions};
|
use fs::{Fs, RemoveOptions};
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use futures::{
|
use futures::{
|
||||||
|
@ -1468,10 +1470,15 @@ impl ExtensionStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(index_json) = serde_json::to_string_pretty(&index) {
|
if let Ok(index_json) = serde_json::to_string_pretty(&index) {
|
||||||
fs.save(&index_path, &index_json.as_str().into(), Default::default())
|
fs.save(
|
||||||
.await
|
&index_path,
|
||||||
.context("failed to save extension index")
|
&index_json.as_str().into(),
|
||||||
.log_err();
|
Default::default(),
|
||||||
|
EncodingWrapper::new(UTF_8),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.context("failed to save extension index")
|
||||||
|
.log_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info!("rebuilt extension index in {:?}", start_time.elapsed());
|
log::info!("rebuilt extension index in {:?}", start_time.elapsed());
|
||||||
|
@ -1636,6 +1643,7 @@ impl ExtensionStore {
|
||||||
&tmp_dir.join(EXTENSION_TOML),
|
&tmp_dir.join(EXTENSION_TOML),
|
||||||
&Rope::from(manifest_toml),
|
&Rope::from(manifest_toml),
|
||||||
language::LineEnding::Unix,
|
language::LineEnding::Unix,
|
||||||
|
EncodingWrapper::new(UTF_8),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -16,6 +16,7 @@ anyhow.workspace = true
|
||||||
async-tar.workspace = true
|
async-tar.workspace = true
|
||||||
async-trait.workspace = true
|
async-trait.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
|
encoding.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
git.workspace = true
|
git.workspace = true
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
|
@ -34,6 +35,9 @@ text.workspace = true
|
||||||
time.workspace = true
|
time.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
workspace-hack.workspace = true
|
workspace-hack.workspace = true
|
||||||
|
schemars.workspace = true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
fsevent.workspace = true
|
fsevent.workspace = true
|
||||||
|
|
96
crates/fs/src/encodings.rs
Normal file
96
crates/fs/src/encodings.rs
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
//! Encoding and decoding utilities using the `encoding` crate.
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use anyhow::{Error, Result};
|
||||||
|
use encoding::Encoding;
|
||||||
|
use serde::{Deserialize, de::Visitor};
|
||||||
|
|
||||||
|
/// A wrapper around `encoding::Encoding` to implement `Send` and `Sync`.
|
||||||
|
/// Since the reference is static, it is safe to send it across threads.
|
||||||
|
pub struct EncodingWrapper(&'static dyn Encoding);
|
||||||
|
|
||||||
|
impl Debug for EncodingWrapper {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_tuple("EncodingWrapper")
|
||||||
|
.field(&self.0.name())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EncodingWrapperVisitor;
|
||||||
|
|
||||||
|
impl<'vi> Visitor<'vi> for EncodingWrapperVisitor {
|
||||||
|
type Value = EncodingWrapper;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
formatter.write_str("a valid encoding name")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E: serde::de::Error>(self, encoding: &str) -> Result<EncodingWrapper, E> {
|
||||||
|
Ok(EncodingWrapper(
|
||||||
|
encoding::label::encoding_from_whatwg_label(encoding)
|
||||||
|
.ok_or_else(|| serde::de::Error::custom("Invalid Encoding"))?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_string<E: serde::de::Error>(self, encoding: String) -> Result<EncodingWrapper, E> {
|
||||||
|
Ok(EncodingWrapper(
|
||||||
|
encoding::label::encoding_from_whatwg_label(&encoding)
|
||||||
|
.ok_or_else(|| serde::de::Error::custom("Invalid Encoding"))?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for EncodingWrapper {
|
||||||
|
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
deserializer.deserialize_str(EncodingWrapperVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for EncodingWrapper {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.0.name() == other.0.name()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for EncodingWrapper {}
|
||||||
|
unsafe impl Sync for EncodingWrapper {}
|
||||||
|
|
||||||
|
impl Clone for EncodingWrapper {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
EncodingWrapper(self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EncodingWrapper {
|
||||||
|
pub fn new(encoding: &'static dyn Encoding) -> EncodingWrapper {
|
||||||
|
EncodingWrapper(encoding)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn decode(&self, input: Vec<u8>) -> Result<String> {
|
||||||
|
match self.0.decode(&input, encoding::DecoderTrap::Replace) {
|
||||||
|
Ok(v) => Ok(v),
|
||||||
|
Err(e) => Err(Error::msg(e.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn encode(&self, input: String) -> Result<Vec<u8>> {
|
||||||
|
match self.0.encode(&input, encoding::EncoderTrap::Replace) {
|
||||||
|
Ok(v) => Ok(v),
|
||||||
|
Err(e) => Err(Error::msg(e.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a byte vector from a specified encoding to a UTF-8 string.
|
||||||
|
pub async fn to_utf8<'a>(input: Vec<u8>, encoding: EncodingWrapper) -> Result<String> {
|
||||||
|
Ok(encoding.decode(input).await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a UTF-8 string to a byte vector in a specified encoding.
|
||||||
|
pub async fn from_utf8<'a>(input: String, target: EncodingWrapper) -> Result<Vec<u8>> {
|
||||||
|
Ok(target.encode(input).await?)
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
mod mac_watcher;
|
mod mac_watcher;
|
||||||
|
|
||||||
|
pub mod encodings;
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
pub mod fs_watcher;
|
pub mod fs_watcher;
|
||||||
|
|
||||||
|
@ -54,6 +55,9 @@ use smol::io::AsyncReadExt;
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
|
|
||||||
|
use crate::encodings::EncodingWrapper;
|
||||||
|
use crate::encodings::from_utf8;
|
||||||
|
|
||||||
pub trait Watcher: Send + Sync {
|
pub trait Watcher: Send + Sync {
|
||||||
fn add(&self, path: &Path) -> Result<()>;
|
fn add(&self, path: &Path) -> Result<()>;
|
||||||
fn remove(&self, path: &Path) -> Result<()>;
|
fn remove(&self, path: &Path) -> Result<()>;
|
||||||
|
@ -108,9 +112,25 @@ pub trait Fs: Send + Sync {
|
||||||
async fn load(&self, path: &Path) -> Result<String> {
|
async fn load(&self, path: &Path) -> Result<String> {
|
||||||
Ok(String::from_utf8(self.load_bytes(path).await?)?)
|
Ok(String::from_utf8(self.load_bytes(path).await?)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Load a file with the specified encoding, returning a UTF-8 string.
|
||||||
|
async fn load_with_encoding(
|
||||||
|
&self,
|
||||||
|
path: PathBuf,
|
||||||
|
encoding: EncodingWrapper,
|
||||||
|
) -> anyhow::Result<String> {
|
||||||
|
Ok(encodings::to_utf8(self.load_bytes(path.as_path()).await?, encoding).await?)
|
||||||
|
}
|
||||||
|
|
||||||
async fn load_bytes(&self, path: &Path) -> Result<Vec<u8>>;
|
async fn load_bytes(&self, path: &Path) -> Result<Vec<u8>>;
|
||||||
async fn atomic_write(&self, path: PathBuf, text: String) -> Result<()>;
|
async fn atomic_write(&self, path: PathBuf, text: String) -> Result<()>;
|
||||||
async fn save(&self, path: &Path, text: &Rope, line_ending: LineEnding) -> Result<()>;
|
async fn save(
|
||||||
|
&self,
|
||||||
|
path: &Path,
|
||||||
|
text: &Rope,
|
||||||
|
line_ending: LineEnding,
|
||||||
|
encoding: EncodingWrapper,
|
||||||
|
) -> Result<()>;
|
||||||
async fn write(&self, path: &Path, content: &[u8]) -> Result<()>;
|
async fn write(&self, path: &Path, content: &[u8]) -> Result<()>;
|
||||||
async fn canonicalize(&self, path: &Path) -> Result<PathBuf>;
|
async fn canonicalize(&self, path: &Path) -> Result<PathBuf>;
|
||||||
async fn is_file(&self, path: &Path) -> bool;
|
async fn is_file(&self, path: &Path) -> bool;
|
||||||
|
@ -539,8 +559,12 @@ impl Fs for RealFs {
|
||||||
|
|
||||||
async fn load(&self, path: &Path) -> Result<String> {
|
async fn load(&self, path: &Path) -> Result<String> {
|
||||||
let path = path.to_path_buf();
|
let path = path.to_path_buf();
|
||||||
let text = smol::unblock(|| std::fs::read_to_string(path)).await?;
|
let encoding = EncodingWrapper::new(encoding::all::UTF_8);
|
||||||
Ok(text)
|
let text =
|
||||||
|
smol::unblock(async || Ok(encodings::to_utf8(std::fs::read(path)?, encoding).await?))
|
||||||
|
.await
|
||||||
|
.await;
|
||||||
|
text
|
||||||
}
|
}
|
||||||
async fn load_bytes(&self, path: &Path) -> Result<Vec<u8>> {
|
async fn load_bytes(&self, path: &Path) -> Result<Vec<u8>> {
|
||||||
let path = path.to_path_buf();
|
let path = path.to_path_buf();
|
||||||
|
@ -594,7 +618,13 @@ impl Fs for RealFs {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn save(&self, path: &Path, text: &Rope, line_ending: LineEnding) -> Result<()> {
|
async fn save(
|
||||||
|
&self,
|
||||||
|
path: &Path,
|
||||||
|
text: &Rope,
|
||||||
|
line_ending: LineEnding,
|
||||||
|
encoding: EncodingWrapper,
|
||||||
|
) -> Result<()> {
|
||||||
let buffer_size = text.summary().len.min(10 * 1024);
|
let buffer_size = text.summary().len.min(10 * 1024);
|
||||||
if let Some(path) = path.parent() {
|
if let Some(path) = path.parent() {
|
||||||
self.create_dir(path).await?;
|
self.create_dir(path).await?;
|
||||||
|
@ -602,7 +632,9 @@ impl Fs for RealFs {
|
||||||
let file = smol::fs::File::create(path).await?;
|
let file = smol::fs::File::create(path).await?;
|
||||||
let mut writer = smol::io::BufWriter::with_capacity(buffer_size, file);
|
let mut writer = smol::io::BufWriter::with_capacity(buffer_size, file);
|
||||||
for chunk in chunks(text, line_ending) {
|
for chunk in chunks(text, line_ending) {
|
||||||
writer.write_all(chunk.as_bytes()).await?;
|
writer
|
||||||
|
.write_all(&from_utf8(chunk.to_string(), encoding.clone()).await?)
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
writer.flush().await?;
|
writer.flush().await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -2273,14 +2305,22 @@ impl Fs for FakeFs {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn save(&self, path: &Path, text: &Rope, line_ending: LineEnding) -> Result<()> {
|
async fn save(
|
||||||
|
&self,
|
||||||
|
path: &Path,
|
||||||
|
text: &Rope,
|
||||||
|
line_ending: LineEnding,
|
||||||
|
encoding: EncodingWrapper,
|
||||||
|
) -> Result<()> {
|
||||||
|
use crate::encodings::from_utf8;
|
||||||
|
|
||||||
self.simulate_random_delay().await;
|
self.simulate_random_delay().await;
|
||||||
let path = normalize_path(path);
|
let path = normalize_path(path);
|
||||||
let content = chunks(text, line_ending).collect::<String>();
|
let content = chunks(text, line_ending).collect::<String>();
|
||||||
if let Some(path) = path.parent() {
|
if let Some(path) = path.parent() {
|
||||||
self.create_dir(path).await?;
|
self.create_dir(path).await?;
|
||||||
}
|
}
|
||||||
self.write_file_internal(path, content.into_bytes(), false)?;
|
self.write_file_internal(path, from_utf8(content, encoding).await?, false)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ command_palette_hooks.workspace = true
|
||||||
component.workspace = true
|
component.workspace = true
|
||||||
db.workspace = true
|
db.workspace = true
|
||||||
editor.workspace = true
|
editor.workspace = true
|
||||||
|
encoding.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
fuzzy.workspace = true
|
fuzzy.workspace = true
|
||||||
git.workspace = true
|
git.workspace = true
|
||||||
|
|
|
@ -362,8 +362,9 @@ impl Render for FileDiffView {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use editor::test::editor_test_context::assert_state_with_diff;
|
use editor::test::editor_test_context::assert_state_with_diff;
|
||||||
|
use encoding::all::UTF_8;
|
||||||
use gpui::TestAppContext;
|
use gpui::TestAppContext;
|
||||||
use project::{FakeFs, Fs, Project};
|
use project::{FakeFs, Fs, Project, encodings::EncodingWrapper};
|
||||||
use settings::{Settings, SettingsStore};
|
use settings::{Settings, SettingsStore};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use unindent::unindent;
|
use unindent::unindent;
|
||||||
|
@ -444,6 +445,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
EncodingWrapper::new(UTF_8),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -479,6 +481,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
EncodingWrapper::new(UTF_8),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -31,7 +31,9 @@ anyhow.workspace = true
|
||||||
async-trait.workspace = true
|
async-trait.workspace = true
|
||||||
clock.workspace = true
|
clock.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
|
diffy = "0.4.2"
|
||||||
ec4rs.workspace = true
|
ec4rs.workspace = true
|
||||||
|
encoding.workspace = true
|
||||||
fs.workspace = true
|
fs.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
fuzzy.workspace = true
|
fuzzy.workspace = true
|
||||||
|
@ -69,7 +71,6 @@ unicase = "2.6"
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
watch.workspace = true
|
watch.workspace = true
|
||||||
workspace-hack.workspace = true
|
workspace-hack.workspace = true
|
||||||
diffy = "0.4.2"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
collections = { workspace = true, features = ["test-support"] }
|
collections = { workspace = true, features = ["test-support"] }
|
||||||
|
|
|
@ -21,6 +21,7 @@ use anyhow::{Context as _, Result};
|
||||||
pub use clock::ReplicaId;
|
pub use clock::ReplicaId;
|
||||||
use clock::{AGENT_REPLICA_ID, Lamport};
|
use clock::{AGENT_REPLICA_ID, Lamport};
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
|
use encoding::Encoding;
|
||||||
use fs::MTime;
|
use fs::MTime;
|
||||||
use futures::channel::oneshot;
|
use futures::channel::oneshot;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
@ -127,6 +128,7 @@ pub struct Buffer {
|
||||||
has_unsaved_edits: Cell<(clock::Global, bool)>,
|
has_unsaved_edits: Cell<(clock::Global, bool)>,
|
||||||
change_bits: Vec<rc::Weak<Cell<bool>>>,
|
change_bits: Vec<rc::Weak<Cell<bool>>>,
|
||||||
_subscriptions: Vec<gpui::Subscription>,
|
_subscriptions: Vec<gpui::Subscription>,
|
||||||
|
pub encoding: &'static dyn encoding::Encoding,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
@ -400,6 +402,10 @@ pub trait LocalFile: File {
|
||||||
|
|
||||||
/// Loads the file's contents from disk.
|
/// Loads the file's contents from disk.
|
||||||
fn load_bytes(&self, cx: &App) -> Task<Result<Vec<u8>>>;
|
fn load_bytes(&self, cx: &App) -> Task<Result<Vec<u8>>>;
|
||||||
|
|
||||||
|
/// Loads the file contents from disk, decoding them with the given encoding.
|
||||||
|
fn load_with_encoding(&self, cx: &App, encoding: &'static dyn Encoding)
|
||||||
|
-> Task<Result<String>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The auto-indent behavior associated with an editing operation.
|
/// The auto-indent behavior associated with an editing operation.
|
||||||
|
@ -958,6 +964,7 @@ impl Buffer {
|
||||||
has_conflict: false,
|
has_conflict: false,
|
||||||
change_bits: Default::default(),
|
change_bits: Default::default(),
|
||||||
_subscriptions: Vec::new(),
|
_subscriptions: Vec::new(),
|
||||||
|
encoding: encoding::all::UTF_8,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1274,12 +1281,15 @@ impl Buffer {
|
||||||
/// Reloads the contents of the buffer from disk.
|
/// Reloads the contents of the buffer from disk.
|
||||||
pub fn reload(&mut self, cx: &Context<Self>) -> oneshot::Receiver<Option<Transaction>> {
|
pub fn reload(&mut self, cx: &Context<Self>) -> oneshot::Receiver<Option<Transaction>> {
|
||||||
let (tx, rx) = futures::channel::oneshot::channel();
|
let (tx, rx) = futures::channel::oneshot::channel();
|
||||||
|
let encoding = self.encoding;
|
||||||
let prev_version = self.text.version();
|
let prev_version = self.text.version();
|
||||||
self.reload_task = Some(cx.spawn(async move |this, cx| {
|
self.reload_task = Some(cx.spawn(async move |this, cx| {
|
||||||
let Some((new_mtime, new_text)) = this.update(cx, |this, cx| {
|
let Some((new_mtime, new_text)) = this.update(cx, |this, cx| {
|
||||||
let file = this.file.as_ref()?.as_local()?;
|
let file = this.file.as_ref()?.as_local()?;
|
||||||
|
Some((
|
||||||
Some((file.disk_state().mtime(), file.load(cx)))
|
file.disk_state().mtime(),
|
||||||
|
file.load_with_encoding(cx, encoding),
|
||||||
|
))
|
||||||
})?
|
})?
|
||||||
else {
|
else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -4965,6 +4975,10 @@ impl LocalFile for TestFile {
|
||||||
fn load_bytes(&self, _cx: &App) -> Task<Result<Vec<u8>>> {
|
fn load_bytes(&self, _cx: &App) -> Task<Result<Vec<u8>>> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_with_encoding(&self, _: &App, _: &'static dyn Encoding) -> Task<Result<String>> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn contiguous_ranges(
|
pub(crate) fn contiguous_ranges(
|
||||||
|
|
|
@ -39,6 +39,7 @@ clock.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
context_server.workspace = true
|
context_server.workspace = true
|
||||||
dap.workspace = true
|
dap.workspace = true
|
||||||
|
encoding.workspace = true
|
||||||
extension.workspace = true
|
extension.workspace = true
|
||||||
fancy-regex.workspace = true
|
fancy-regex.workspace = true
|
||||||
fs.workspace = true
|
fs.workspace = true
|
||||||
|
@ -90,6 +91,7 @@ worktree.workspace = true
|
||||||
zlog.workspace = true
|
zlog.workspace = true
|
||||||
workspace-hack.workspace = true
|
workspace-hack.workspace = true
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
client = { workspace = true, features = ["test-support"] }
|
client = { workspace = true, features = ["test-support"] }
|
||||||
collections = { workspace = true, features = ["test-support"] }
|
collections = { workspace = true, features = ["test-support"] }
|
||||||
|
|
|
@ -372,6 +372,8 @@ impl LocalBufferStore {
|
||||||
let version = buffer.version();
|
let version = buffer.version();
|
||||||
let buffer_id = buffer.remote_id();
|
let buffer_id = buffer.remote_id();
|
||||||
let file = buffer.file().cloned();
|
let file = buffer.file().cloned();
|
||||||
|
let encoding = buffer.encoding;
|
||||||
|
|
||||||
if file
|
if file
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.is_some_and(|file| file.disk_state() == DiskState::New)
|
.is_some_and(|file| file.disk_state() == DiskState::New)
|
||||||
|
@ -380,7 +382,7 @@ impl LocalBufferStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
let save = worktree.update(cx, |worktree, cx| {
|
let save = worktree.update(cx, |worktree, cx| {
|
||||||
worktree.write_file(path.as_ref(), text, line_ending, cx)
|
worktree.write_file(path.as_ref(), text, line_ending, cx, encoding)
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.spawn(async move |this, cx| {
|
cx.spawn(async move |this, cx| {
|
||||||
|
|
|
@ -7,7 +7,8 @@ use std::{
|
||||||
|
|
||||||
use anyhow::{Context as _, Result, anyhow};
|
use anyhow::{Context as _, Result, anyhow};
|
||||||
use collections::{HashMap, HashSet};
|
use collections::{HashMap, HashSet};
|
||||||
use fs::Fs;
|
use encoding::all::UTF_8;
|
||||||
|
use fs::{Fs, encodings::EncodingWrapper};
|
||||||
use futures::{
|
use futures::{
|
||||||
FutureExt,
|
FutureExt,
|
||||||
future::{self, Shared},
|
future::{self, Shared},
|
||||||
|
@ -941,10 +942,12 @@ async fn install_prettier_packages(
|
||||||
|
|
||||||
async fn save_prettier_server_file(fs: &dyn Fs) -> anyhow::Result<()> {
|
async fn save_prettier_server_file(fs: &dyn Fs) -> anyhow::Result<()> {
|
||||||
let prettier_wrapper_path = default_prettier_dir().join(prettier::PRETTIER_SERVER_FILE);
|
let prettier_wrapper_path = default_prettier_dir().join(prettier::PRETTIER_SERVER_FILE);
|
||||||
|
let encoding_wrapper = EncodingWrapper::new(UTF_8);
|
||||||
fs.save(
|
fs.save(
|
||||||
&prettier_wrapper_path,
|
&prettier_wrapper_path,
|
||||||
&text::Rope::from(prettier::PRETTIER_SERVER_JS),
|
&text::Rope::from(prettier::PRETTIER_SERVER_JS),
|
||||||
text::LineEnding::Unix,
|
text::LineEnding::Unix,
|
||||||
|
encoding_wrapper,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.with_context(|| {
|
.with_context(|| {
|
||||||
|
|
|
@ -9,7 +9,8 @@ use buffer_diff::{
|
||||||
BufferDiffEvent, CALCULATE_DIFF_TASK, DiffHunkSecondaryStatus, DiffHunkStatus,
|
BufferDiffEvent, CALCULATE_DIFF_TASK, DiffHunkSecondaryStatus, DiffHunkStatus,
|
||||||
DiffHunkStatusKind, assert_hunks,
|
DiffHunkStatusKind, assert_hunks,
|
||||||
};
|
};
|
||||||
use fs::FakeFs;
|
use encoding::all::UTF_8;
|
||||||
|
use fs::{FakeFs, encodings::EncodingWrapper};
|
||||||
use futures::{StreamExt, future};
|
use futures::{StreamExt, future};
|
||||||
use git::{
|
use git::{
|
||||||
GitHostingProviderRegistry,
|
GitHostingProviderRegistry,
|
||||||
|
@ -1448,10 +1449,14 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
let encoding_wrapper = EncodingWrapper::new(UTF_8);
|
||||||
|
|
||||||
fs.save(
|
fs.save(
|
||||||
path!("/the-root/Cargo.lock").as_ref(),
|
path!("/the-root/Cargo.lock").as_ref(),
|
||||||
&"".into(),
|
&"".into(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
encoding_wrapper.clone(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -1459,6 +1464,7 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon
|
||||||
path!("/the-stdlib/LICENSE").as_ref(),
|
path!("/the-stdlib/LICENSE").as_ref(),
|
||||||
&"".into(),
|
&"".into(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
encoding_wrapper.clone(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -1466,6 +1472,7 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon
|
||||||
path!("/the/stdlib/src/string.rs").as_ref(),
|
path!("/the/stdlib/src/string.rs").as_ref(),
|
||||||
&"".into(),
|
&"".into(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
encoding_wrapper,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -3941,12 +3948,15 @@ async fn test_file_changes_multiple_times_on_disk(cx: &mut gpui::TestAppContext)
|
||||||
// the next file change occurs.
|
// the next file change occurs.
|
||||||
cx.executor().deprioritize(*language::BUFFER_DIFF_TASK);
|
cx.executor().deprioritize(*language::BUFFER_DIFF_TASK);
|
||||||
|
|
||||||
|
let encoding_wrapper = EncodingWrapper::new(UTF_8);
|
||||||
|
|
||||||
// Change the buffer's file on disk, and then wait for the file change
|
// Change the buffer's file on disk, and then wait for the file change
|
||||||
// to be detected by the worktree, so that the buffer starts reloading.
|
// to be detected by the worktree, so that the buffer starts reloading.
|
||||||
fs.save(
|
fs.save(
|
||||||
path!("/dir/file1").as_ref(),
|
path!("/dir/file1").as_ref(),
|
||||||
&"the first contents".into(),
|
&"the first contents".into(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
encoding_wrapper.clone(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -3958,6 +3968,7 @@ async fn test_file_changes_multiple_times_on_disk(cx: &mut gpui::TestAppContext)
|
||||||
path!("/dir/file1").as_ref(),
|
path!("/dir/file1").as_ref(),
|
||||||
&"the second contents".into(),
|
&"the second contents".into(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
encoding_wrapper,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -3996,12 +4007,15 @@ async fn test_edit_buffer_while_it_reloads(cx: &mut gpui::TestAppContext) {
|
||||||
// the next file change occurs.
|
// the next file change occurs.
|
||||||
cx.executor().deprioritize(*language::BUFFER_DIFF_TASK);
|
cx.executor().deprioritize(*language::BUFFER_DIFF_TASK);
|
||||||
|
|
||||||
|
let encoding_wrapper = EncodingWrapper::new(UTF_8);
|
||||||
|
|
||||||
// Change the buffer's file on disk, and then wait for the file change
|
// Change the buffer's file on disk, and then wait for the file change
|
||||||
// to be detected by the worktree, so that the buffer starts reloading.
|
// to be detected by the worktree, so that the buffer starts reloading.
|
||||||
fs.save(
|
fs.save(
|
||||||
path!("/dir/file1").as_ref(),
|
path!("/dir/file1").as_ref(),
|
||||||
&"the first contents".into(),
|
&"the first contents".into(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
encoding_wrapper,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -4603,10 +4617,14 @@ async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) {
|
||||||
|
|
||||||
let (new_contents, new_offsets) =
|
let (new_contents, new_offsets) =
|
||||||
marked_text_offsets("oneˇ\nthree ˇFOURˇ five\nsixtyˇ seven\n");
|
marked_text_offsets("oneˇ\nthree ˇFOURˇ five\nsixtyˇ seven\n");
|
||||||
|
|
||||||
|
let encoding_wrapper = EncodingWrapper::new(UTF_8);
|
||||||
|
|
||||||
fs.save(
|
fs.save(
|
||||||
path!("/dir/the-file").as_ref(),
|
path!("/dir/the-file").as_ref(),
|
||||||
&new_contents.as_str().into(),
|
&new_contents.as_str().into(),
|
||||||
LineEnding::Unix,
|
LineEnding::Unix,
|
||||||
|
encoding_wrapper,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -4634,11 +4652,14 @@ async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) {
|
||||||
assert!(!buffer.has_conflict());
|
assert!(!buffer.has_conflict());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let encoding_wrapper = EncodingWrapper::new(UTF_8);
|
||||||
|
|
||||||
// Change the file on disk again, adding blank lines to the beginning.
|
// Change the file on disk again, adding blank lines to the beginning.
|
||||||
fs.save(
|
fs.save(
|
||||||
path!("/dir/the-file").as_ref(),
|
path!("/dir/the-file").as_ref(),
|
||||||
&"\n\n\nAAAA\naaa\nBB\nbbbbb\n".into(),
|
&"\n\n\nAAAA\naaa\nBB\nbbbbb\n".into(),
|
||||||
LineEnding::Unix,
|
LineEnding::Unix,
|
||||||
|
encoding_wrapper,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -4685,12 +4706,15 @@ async fn test_buffer_line_endings(cx: &mut gpui::TestAppContext) {
|
||||||
assert_eq!(buffer.line_ending(), LineEnding::Windows);
|
assert_eq!(buffer.line_ending(), LineEnding::Windows);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let encoding_wrapper = EncodingWrapper::new(UTF_8);
|
||||||
|
|
||||||
// Change a file's line endings on disk from unix to windows. The buffer's
|
// Change a file's line endings on disk from unix to windows. The buffer's
|
||||||
// state updates correctly.
|
// state updates correctly.
|
||||||
fs.save(
|
fs.save(
|
||||||
path!("/dir/file1").as_ref(),
|
path!("/dir/file1").as_ref(),
|
||||||
&"aaa\nb\nc\n".into(),
|
&"aaa\nb\nc\n".into(),
|
||||||
LineEnding::Windows,
|
LineEnding::Windows,
|
||||||
|
encoding_wrapper,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -30,6 +30,7 @@ clap.workspace = true
|
||||||
client.workspace = true
|
client.workspace = true
|
||||||
dap_adapters.workspace = true
|
dap_adapters.workspace = true
|
||||||
debug_adapter_extension.workspace = true
|
debug_adapter_extension.workspace = true
|
||||||
|
encoding.workspace = true
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
extension.workspace = true
|
extension.workspace = true
|
||||||
extension_host.workspace = true
|
extension_host.workspace = true
|
||||||
|
|
|
@ -6,10 +6,11 @@ use assistant_tool::{Tool as _, ToolResultContent};
|
||||||
use assistant_tools::{ReadFileTool, ReadFileToolInput};
|
use assistant_tools::{ReadFileTool, ReadFileToolInput};
|
||||||
use client::{Client, UserStore};
|
use client::{Client, UserStore};
|
||||||
use clock::FakeSystemClock;
|
use clock::FakeSystemClock;
|
||||||
|
use encoding::all::UTF_8;
|
||||||
use language_model::{LanguageModelRequest, fake_provider::FakeLanguageModel};
|
use language_model::{LanguageModelRequest, fake_provider::FakeLanguageModel};
|
||||||
|
|
||||||
use extension::ExtensionHostProxy;
|
use extension::ExtensionHostProxy;
|
||||||
use fs::{FakeFs, Fs};
|
use fs::{FakeFs, Fs, encodings::EncodingWrapper};
|
||||||
use gpui::{AppContext as _, Entity, SemanticVersion, TestAppContext};
|
use gpui::{AppContext as _, Entity, SemanticVersion, TestAppContext};
|
||||||
use http_client::{BlockedHttpClient, FakeHttpClient};
|
use http_client::{BlockedHttpClient, FakeHttpClient};
|
||||||
use language::{
|
use language::{
|
||||||
|
@ -123,6 +124,7 @@ async fn test_basic_remote_editing(cx: &mut TestAppContext, server_cx: &mut Test
|
||||||
path!("/code/project1/src/main.rs").as_ref(),
|
path!("/code/project1/src/main.rs").as_ref(),
|
||||||
&"fn main() {}".into(),
|
&"fn main() {}".into(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
EncodingWrapper::new(UTF_8),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -763,6 +765,7 @@ async fn test_remote_reload(cx: &mut TestAppContext, server_cx: &mut TestAppCont
|
||||||
&PathBuf::from(path!("/code/project1/src/lib.rs")),
|
&PathBuf::from(path!("/code/project1/src/lib.rs")),
|
||||||
&("bangles".to_string().into()),
|
&("bangles".to_string().into()),
|
||||||
LineEnding::Unix,
|
LineEnding::Unix,
|
||||||
|
EncodingWrapper::new(UTF_8),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -778,6 +781,7 @@ async fn test_remote_reload(cx: &mut TestAppContext, server_cx: &mut TestAppCont
|
||||||
&PathBuf::from(path!("/code/project1/src/lib.rs")),
|
&PathBuf::from(path!("/code/project1/src/lib.rs")),
|
||||||
&("bloop".to_string().into()),
|
&("bloop".to_string().into()),
|
||||||
LineEnding::Unix,
|
LineEnding::Unix,
|
||||||
|
EncodingWrapper::new(UTF_8),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -52,6 +52,7 @@ workspace.workspace = true
|
||||||
zed_actions.workspace = true
|
zed_actions.workspace = true
|
||||||
workspace-hack.workspace = true
|
workspace-hack.workspace = true
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
assets.workspace = true
|
assets.workspace = true
|
||||||
command_palette.workspace = true
|
command_palette.workspace = true
|
||||||
|
|
|
@ -35,6 +35,7 @@ clock.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
component.workspace = true
|
component.workspace = true
|
||||||
db.workspace = true
|
db.workspace = true
|
||||||
|
encoding.workspace = true
|
||||||
fs.workspace = true
|
fs.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
|
|
|
@ -19,6 +19,8 @@ mod workspace_settings;
|
||||||
|
|
||||||
pub use crate::notifications::NotificationFrame;
|
pub use crate::notifications::NotificationFrame;
|
||||||
pub use dock::Panel;
|
pub use dock::Panel;
|
||||||
|
use encoding::all::UTF_8;
|
||||||
|
use fs::encodings::EncodingWrapper;
|
||||||
pub use path_list::PathList;
|
pub use path_list::PathList;
|
||||||
pub use toast_layer::{ToastAction, ToastLayer, ToastView};
|
pub use toast_layer::{ToastAction, ToastLayer, ToastView};
|
||||||
|
|
||||||
|
@ -7232,8 +7234,14 @@ pub fn create_and_open_local_file(
|
||||||
let fs = workspace.read_with(cx, |workspace, _| workspace.app_state().fs.clone())?;
|
let fs = workspace.read_with(cx, |workspace, _| workspace.app_state().fs.clone())?;
|
||||||
if !fs.is_file(path).await {
|
if !fs.is_file(path).await {
|
||||||
fs.create_file(path, Default::default()).await?;
|
fs.create_file(path, Default::default()).await?;
|
||||||
fs.save(path, &default_content(), Default::default())
|
let encoding_wrapper = EncodingWrapper::new(UTF_8);
|
||||||
.await?;
|
fs.save(
|
||||||
|
path,
|
||||||
|
&default_content(),
|
||||||
|
Default::default(),
|
||||||
|
encoding_wrapper,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut items = workspace
|
let mut items = workspace
|
||||||
|
|
|
@ -26,6 +26,7 @@ test-support = [
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
clock.workspace = true
|
clock.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
|
encoding.workspace = true
|
||||||
fs.workspace = true
|
fs.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
fuzzy.workspace = true
|
fuzzy.workspace = true
|
||||||
|
@ -49,6 +50,7 @@ text.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
workspace-hack.workspace = true
|
workspace-hack.workspace = true
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
clock = { workspace = true, features = ["test-support"] }
|
clock = { workspace = true, features = ["test-support"] }
|
||||||
collections = { workspace = true, features = ["test-support"] }
|
collections = { workspace = true, features = ["test-support"] }
|
||||||
|
|
|
@ -7,7 +7,11 @@ use ::ignore::gitignore::{Gitignore, GitignoreBuilder};
|
||||||
use anyhow::{Context as _, Result, anyhow};
|
use anyhow::{Context as _, Result, anyhow};
|
||||||
use clock::ReplicaId;
|
use clock::ReplicaId;
|
||||||
use collections::{HashMap, HashSet, VecDeque};
|
use collections::{HashMap, HashSet, VecDeque};
|
||||||
use fs::{Fs, MTime, PathEvent, RemoveOptions, Watcher, copy_recursive, read_dir_items};
|
use encoding::Encoding;
|
||||||
|
use fs::{
|
||||||
|
Fs, MTime, PathEvent, RemoveOptions, Watcher, copy_recursive, encodings::EncodingWrapper,
|
||||||
|
read_dir_items,
|
||||||
|
};
|
||||||
use futures::{
|
use futures::{
|
||||||
FutureExt as _, Stream, StreamExt,
|
FutureExt as _, Stream, StreamExt,
|
||||||
channel::{
|
channel::{
|
||||||
|
@ -831,9 +835,10 @@ impl Worktree {
|
||||||
text: Rope,
|
text: Rope,
|
||||||
line_ending: LineEnding,
|
line_ending: LineEnding,
|
||||||
cx: &Context<Worktree>,
|
cx: &Context<Worktree>,
|
||||||
|
encoding: &'static dyn Encoding,
|
||||||
) -> Task<Result<Arc<File>>> {
|
) -> Task<Result<Arc<File>>> {
|
||||||
match self {
|
match self {
|
||||||
Worktree::Local(this) => this.write_file(path, text, line_ending, cx),
|
Worktree::Local(this) => this.write_file(path, text, line_ending, cx, encoding),
|
||||||
Worktree::Remote(_) => {
|
Worktree::Remote(_) => {
|
||||||
Task::ready(Err(anyhow!("remote worktree can't yet write files")))
|
Task::ready(Err(anyhow!("remote worktree can't yet write files")))
|
||||||
}
|
}
|
||||||
|
@ -1644,6 +1649,7 @@ impl LocalWorktree {
|
||||||
text: Rope,
|
text: Rope,
|
||||||
line_ending: LineEnding,
|
line_ending: LineEnding,
|
||||||
cx: &Context<Worktree>,
|
cx: &Context<Worktree>,
|
||||||
|
encoding: &'static dyn Encoding,
|
||||||
) -> Task<Result<Arc<File>>> {
|
) -> Task<Result<Arc<File>>> {
|
||||||
let path = path.into();
|
let path = path.into();
|
||||||
let fs = self.fs.clone();
|
let fs = self.fs.clone();
|
||||||
|
@ -1652,10 +1658,15 @@ impl LocalWorktree {
|
||||||
return Task::ready(Err(anyhow!("invalid path {path:?}")));
|
return Task::ready(Err(anyhow!("invalid path {path:?}")));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let encoding_wrapper = EncodingWrapper::new(encoding);
|
||||||
|
|
||||||
let write = cx.background_spawn({
|
let write = cx.background_spawn({
|
||||||
let fs = fs.clone();
|
let fs = fs.clone();
|
||||||
let abs_path = abs_path.clone();
|
let abs_path = abs_path.clone();
|
||||||
async move { fs.save(&abs_path, &text, line_ending).await }
|
async move {
|
||||||
|
fs.save(&abs_path, &text, line_ending, encoding_wrapper)
|
||||||
|
.await
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.spawn(async move |this, cx| {
|
cx.spawn(async move |this, cx| {
|
||||||
|
@ -3361,6 +3372,19 @@ impl language::LocalFile for File {
|
||||||
let fs = worktree.fs.clone();
|
let fs = worktree.fs.clone();
|
||||||
cx.background_spawn(async move { fs.load_bytes(&abs_path?).await })
|
cx.background_spawn(async move { fs.load_bytes(&abs_path?).await })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_with_encoding(
|
||||||
|
&self,
|
||||||
|
cx: &App,
|
||||||
|
encoding: &'static dyn Encoding,
|
||||||
|
) -> Task<Result<String>> {
|
||||||
|
let worktree = self.worktree.read(cx).as_local().unwrap();
|
||||||
|
let path = worktree.absolutize(&self.path);
|
||||||
|
let fs = worktree.fs.clone();
|
||||||
|
|
||||||
|
let encoding = EncodingWrapper::new(encoding);
|
||||||
|
cx.background_spawn(async move { fs.load_with_encoding(path?, encoding).await })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl File {
|
impl File {
|
||||||
|
|
|
@ -3,7 +3,8 @@ use crate::{
|
||||||
worktree_settings::WorktreeSettings,
|
worktree_settings::WorktreeSettings,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use fs::{FakeFs, Fs, RealFs, RemoveOptions};
|
use encoding::all::UTF_8;
|
||||||
|
use fs::{FakeFs, Fs, RealFs, RemoveOptions, encodings::EncodingWrapper};
|
||||||
use git::GITIGNORE;
|
use git::GITIGNORE;
|
||||||
use gpui::{AppContext as _, BackgroundExecutor, BorrowAppContext, Context, Task, TestAppContext};
|
use gpui::{AppContext as _, BackgroundExecutor, BorrowAppContext, Context, Task, TestAppContext};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
@ -642,9 +643,15 @@ async fn test_dirs_no_longer_ignored(cx: &mut TestAppContext) {
|
||||||
|
|
||||||
// Update the gitignore so that node_modules is no longer ignored,
|
// Update the gitignore so that node_modules is no longer ignored,
|
||||||
// but a subdirectory is ignored
|
// but a subdirectory is ignored
|
||||||
fs.save("/root/.gitignore".as_ref(), &"e".into(), Default::default())
|
let encoding_wrapper = fs::encodings::EncodingWrapper::new(UTF_8);
|
||||||
.await
|
fs.save(
|
||||||
.unwrap();
|
"/root/.gitignore".as_ref(),
|
||||||
|
&"e".into(),
|
||||||
|
Default::default(),
|
||||||
|
encoding_wrapper,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
cx.executor().run_until_parked();
|
cx.executor().run_until_parked();
|
||||||
|
|
||||||
// All of the directories that are no longer ignored are now loaded.
|
// All of the directories that are no longer ignored are now loaded.
|
||||||
|
@ -715,6 +722,7 @@ async fn test_write_file(cx: &mut TestAppContext) {
|
||||||
"hello".into(),
|
"hello".into(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
cx,
|
cx,
|
||||||
|
UTF_8,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
@ -726,6 +734,7 @@ async fn test_write_file(cx: &mut TestAppContext) {
|
||||||
"world".into(),
|
"world".into(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
cx,
|
cx,
|
||||||
|
UTF_8,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
@ -1746,8 +1755,13 @@ fn randomly_mutate_worktree(
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
log::info!("overwriting file {:?} ({})", entry.path, entry.id.0);
|
log::info!("overwriting file {:?} ({})", entry.path, entry.id.0);
|
||||||
let task =
|
let task = worktree.write_file(
|
||||||
worktree.write_file(entry.path.clone(), "".into(), Default::default(), cx);
|
entry.path.clone(),
|
||||||
|
"".into(),
|
||||||
|
Default::default(),
|
||||||
|
cx,
|
||||||
|
UTF_8,
|
||||||
|
);
|
||||||
cx.background_spawn(async move {
|
cx.background_spawn(async move {
|
||||||
task.await?;
|
task.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1834,10 +1848,12 @@ async fn randomly_mutate_fs(
|
||||||
ignore_path.strip_prefix(root_path).unwrap(),
|
ignore_path.strip_prefix(root_path).unwrap(),
|
||||||
ignore_contents
|
ignore_contents
|
||||||
);
|
);
|
||||||
|
let encoding_wrapper = EncodingWrapper::new(UTF_8);
|
||||||
fs.save(
|
fs.save(
|
||||||
&ignore_path,
|
&ignore_path,
|
||||||
&ignore_contents.as_str().into(),
|
&ignore_contents.as_str().into(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
encoding_wrapper,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -55,6 +55,8 @@ debugger_tools.workspace = true
|
||||||
debugger_ui.workspace = true
|
debugger_ui.workspace = true
|
||||||
diagnostics.workspace = true
|
diagnostics.workspace = true
|
||||||
editor.workspace = true
|
editor.workspace = true
|
||||||
|
encoding.workspace = true
|
||||||
|
encodings.workspace = true
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
extension.workspace = true
|
extension.workspace = true
|
||||||
extension_host.workspace = true
|
extension_host.workspace = true
|
||||||
|
|
|
@ -397,6 +397,9 @@ pub fn initialize_workspace(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let encoding_indicator =
|
||||||
|
cx.new(|_cx| encodings::EncodingIndicator::new(None, workspace.weak_handle(), None));
|
||||||
|
|
||||||
let cursor_position =
|
let cursor_position =
|
||||||
cx.new(|_| go_to_line::cursor_position::CursorPosition::new(workspace));
|
cx.new(|_| go_to_line::cursor_position::CursorPosition::new(workspace));
|
||||||
workspace.status_bar().update(cx, |status_bar, cx| {
|
workspace.status_bar().update(cx, |status_bar, cx| {
|
||||||
|
@ -409,6 +412,7 @@ pub fn initialize_workspace(
|
||||||
status_bar.add_right_item(active_toolchain_language, window, cx);
|
status_bar.add_right_item(active_toolchain_language, window, cx);
|
||||||
status_bar.add_right_item(vim_mode_indicator, window, cx);
|
status_bar.add_right_item(vim_mode_indicator, window, cx);
|
||||||
status_bar.add_right_item(cursor_position, window, cx);
|
status_bar.add_right_item(cursor_position, window, cx);
|
||||||
|
status_bar.add_right_item(encoding_indicator, window, cx);
|
||||||
status_bar.add_right_item(image_info, window, cx);
|
status_bar.add_right_item(image_info, window, cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1933,6 +1937,8 @@ mod tests {
|
||||||
use assets::Assets;
|
use assets::Assets;
|
||||||
use collections::HashSet;
|
use collections::HashSet;
|
||||||
use editor::{DisplayPoint, Editor, SelectionEffects, display_map::DisplayRow};
|
use editor::{DisplayPoint, Editor, SelectionEffects, display_map::DisplayRow};
|
||||||
|
use encoding::all::UTF_8;
|
||||||
|
use fs::encodings::EncodingWrapper;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
Action, AnyWindowHandle, App, AssetSource, BorrowAppContext, SemanticVersion,
|
Action, AnyWindowHandle, App, AssetSource, BorrowAppContext, SemanticVersion,
|
||||||
TestAppContext, UpdateGlobal, VisualTestContext, WindowHandle, actions,
|
TestAppContext, UpdateGlobal, VisualTestContext, WindowHandle, actions,
|
||||||
|
@ -4124,6 +4130,7 @@ mod tests {
|
||||||
"/settings.json".as_ref(),
|
"/settings.json".as_ref(),
|
||||||
&r#"{"base_keymap": "Atom"}"#.into(),
|
&r#"{"base_keymap": "Atom"}"#.into(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
EncodingWrapper::new(UTF_8),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -4134,6 +4141,7 @@ mod tests {
|
||||||
"/keymap.json".as_ref(),
|
"/keymap.json".as_ref(),
|
||||||
&r#"[{"bindings": {"backspace": "test_only::ActionA"}}]"#.into(),
|
&r#"[{"bindings": {"backspace": "test_only::ActionA"}}]"#.into(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
EncodingWrapper::new(UTF_8),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -4182,6 +4190,7 @@ mod tests {
|
||||||
"/keymap.json".as_ref(),
|
"/keymap.json".as_ref(),
|
||||||
&r#"[{"bindings": {"backspace": "test_only::ActionB"}}]"#.into(),
|
&r#"[{"bindings": {"backspace": "test_only::ActionB"}}]"#.into(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
EncodingWrapper::new(UTF_8),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -4202,6 +4211,7 @@ mod tests {
|
||||||
"/settings.json".as_ref(),
|
"/settings.json".as_ref(),
|
||||||
&r#"{"base_keymap": "JetBrains"}"#.into(),
|
&r#"{"base_keymap": "JetBrains"}"#.into(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
EncodingWrapper::new(UTF_8),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -4242,6 +4252,7 @@ mod tests {
|
||||||
"/settings.json".as_ref(),
|
"/settings.json".as_ref(),
|
||||||
&r#"{"base_keymap": "Atom"}"#.into(),
|
&r#"{"base_keymap": "Atom"}"#.into(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
EncodingWrapper::new(UTF_8),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -4251,6 +4262,7 @@ mod tests {
|
||||||
"/keymap.json".as_ref(),
|
"/keymap.json".as_ref(),
|
||||||
&r#"[{"bindings": {"backspace": "test_only::ActionA"}}]"#.into(),
|
&r#"[{"bindings": {"backspace": "test_only::ActionA"}}]"#.into(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
EncodingWrapper::new(UTF_8),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -4294,6 +4306,7 @@ mod tests {
|
||||||
"/keymap.json".as_ref(),
|
"/keymap.json".as_ref(),
|
||||||
&r#"[{"bindings": {"backspace": null}}]"#.into(),
|
&r#"[{"bindings": {"backspace": null}}]"#.into(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
EncodingWrapper::new(UTF_8),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -4314,6 +4327,7 @@ mod tests {
|
||||||
"/settings.json".as_ref(),
|
"/settings.json".as_ref(),
|
||||||
&r#"{"base_keymap": "JetBrains"}"#.into(),
|
&r#"{"base_keymap": "JetBrains"}"#.into(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
EncodingWrapper::new(UTF_8),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -1278,7 +1278,8 @@ Each option controls displaying of a particular toolbar element. If all elements
|
||||||
```json
|
```json
|
||||||
"status_bar": {
|
"status_bar": {
|
||||||
"active_language_button": true,
|
"active_language_button": true,
|
||||||
"cursor_position_button": true
|
"cursor_position_button": true,
|
||||||
|
"encoding_indicator": true,
|
||||||
},
|
},
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue