From d5974f4c9124a12782d85849f8e1937e4dbaf9ae Mon Sep 17 00:00:00 2001 From: Matt Peterson Date: Wed, 10 Nov 2021 14:19:52 -0700 Subject: [PATCH] Automatically convert paths dropped on WSL instances (#11625) Drag and drop does not work for WSL because paths are pasted as windows paths having incorrect path separator and path root. This PR adds code to correct the path in TerminalControl before pasting to WSL terminals. One problem with this approach is that it assumes the default WSL automount root of "/mnt". It would be possible to add a setting like "WslDragAndDropMountRoot"... but I decided it if someone wants to change automount location it would be simple enough just to create the "/mnt" symlink in WSL. ## Validation Couldn't find an obvious place to add a test. Manually tested cut-n-paste from following paths: - "c:\" - "c:\subdir" - "c:\subdir\subdir" - "\\wsl.localhost\" - \\wsl.localhost\\subdir" Closes #331 --- .github/actions/spelling/allow/allow.txt | 1 + .../TerminalControl/IControlSettings.idl | 1 + src/cascadia/TerminalControl/TermControl.cpp | 37 +++++++++++++++++++ .../TerminalSettings.cpp | 1 + .../TerminalSettingsModel/TerminalSettings.h | 2 + .../UnitTests_Control/MockControlSettings.h | 1 + 6 files changed, 43 insertions(+) diff --git a/.github/actions/spelling/allow/allow.txt b/.github/actions/spelling/allow/allow.txt index 35742a6111e..085ff668247 100644 --- a/.github/actions/spelling/allow/allow.txt +++ b/.github/actions/spelling/allow/allow.txt @@ -41,6 +41,7 @@ Lmid Lorigin maxed mkmk +mnt mru noreply nje diff --git a/src/cascadia/TerminalControl/IControlSettings.idl b/src/cascadia/TerminalControl/IControlSettings.idl index 1b67df91f0e..2b16e733493 100644 --- a/src/cascadia/TerminalControl/IControlSettings.idl +++ b/src/cascadia/TerminalControl/IControlSettings.idl @@ -27,6 +27,7 @@ namespace Microsoft.Terminal.Control interface IControlSettings requires Microsoft.Terminal.Core.ICoreSettings, Microsoft.Terminal.Control.IControlAppearance { String ProfileName; + String ProfileSource; Boolean UseAcrylic; ScrollbarState ScrollState; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index afae49d9dae..7da2257b459 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -2271,6 +2271,42 @@ namespace winrt::Microsoft::Terminal::Control::implementation } std::wstring fullPath{ item.Path() }; + + // Fix path for WSL + if (_settings.ProfileSource() == L"Windows.Terminal.Wsl") + { + std::replace(fullPath.begin(), fullPath.end(), L'\\', L'/'); + + if (fullPath.size() >= 2 && fullPath.at(1) == L':') + { + // C:/foo/bar -> Cc/foo/bar + fullPath.at(1) = til::tolower_ascii(fullPath.at(0)); + // Cc/foo/bar -> /mnt/c/foo/bar + fullPath.replace(0, 1, L"/mnt/"); + } + else + { + static constexpr std::wstring_view wslPathPrefixes[] = { L"//wsl.localhost/", L"//wsl$/" }; + for (auto prefix : wslPathPrefixes) + { + if (til::starts_with(fullPath, prefix)) + { + if (const auto idx = fullPath.find(L'/', prefix.size()); idx != std::wstring::npos) + { + // //wsl.localhost/Ubuntu-18.04/foo/bar -> /foo/bar + fullPath.erase(0, idx); + } + else + { + // //wsl.localhost/Ubuntu-18.04 -> / + fullPath = L"/"; + } + break; + } + } + } + } + const auto containsSpaces = std::find(fullPath.begin(), fullPath.end(), L' ') != fullPath.end(); @@ -2283,6 +2319,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation allPaths += fullPath; } + _core.PasteText(winrt::hstring{ allPaths }); } } diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp index 5e4d1eeea8a..ae995398641 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp @@ -275,6 +275,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // Fill in the remaining properties from the profile _ProfileName = profile.Name(); + _ProfileSource = profile.Source(); _UseAcrylic = profile.UseAcrylic(); _FontFace = profile.FontInfo().FontFace(); diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.h b/src/cascadia/TerminalSettingsModel/TerminalSettings.h index 90b7b60fef7..096f18f7988 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.h @@ -119,6 +119,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // ------------------------ End of Core Settings ----------------------- INHERITABLE_SETTING(Model::TerminalSettings, hstring, ProfileName); + INHERITABLE_SETTING(Model::TerminalSettings, hstring, ProfileSource); + INHERITABLE_SETTING(Model::TerminalSettings, bool, UseAcrylic, false); INHERITABLE_SETTING(Model::TerminalSettings, double, Opacity, UseAcrylic() ? 0.5 : 1.0); INHERITABLE_SETTING(Model::TerminalSettings, hstring, Padding, DEFAULT_PADDING); diff --git a/src/cascadia/UnitTests_Control/MockControlSettings.h b/src/cascadia/UnitTests_Control/MockControlSettings.h index 485f9b146b8..983d7a9d72c 100644 --- a/src/cascadia/UnitTests_Control/MockControlSettings.h +++ b/src/cascadia/UnitTests_Control/MockControlSettings.h @@ -51,6 +51,7 @@ namespace ControlUnitTests // ------------------------ End of Core Settings ----------------------- WINRT_PROPERTY(winrt::hstring, ProfileName); + WINRT_PROPERTY(winrt::hstring, ProfileSource); WINRT_PROPERTY(bool, UseAcrylic, false); WINRT_PROPERTY(double, Opacity, .5); WINRT_PROPERTY(winrt::hstring, Padding, DEFAULT_PADDING);