Skip to content

Commit de586d6

Browse files
committed
Add a new (advanced) profile setting, pathTranslationStyle (#18195)
`pathTranslationStyle` has four options: - `none`: Do no translation - `wsl`: Translate `C:\` to `/mnt/c` and `\\wsl$\Foo\bar` to `/bar` - `cygwin`: Translate `C:\` to `/cygdrive/c` - `msys2`: Translate `C:\` to `/c` It is intended as a broadly-supported replacement for us checking the source every time the user drops a path. We no longer need to push the source name all the way down to the control. I am hesitant to commit to using other folks' product names in our settings model, however, these are almost certainly more recognizable than whatever other weird names we could come up with. The Git Bash fragment extension profile could conceivably use `pathTranslationStyle` `msys2` to make sure drag/dropped paths look right. (cherry picked from commit 0689067) Service-Card-Id: PVTI_lADOAF3p4s4AmhmQzgW9l6A Service-Version: 1.22
1 parent 6e406dd commit de586d6

22 files changed

+162
-88
lines changed

.github/actions/spelling/expect/expect.txt

+1
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ CXVIRTUALSCREEN
341341
CXVSCROLL
342342
CYFRAME
343343
CYFULLSCREEN
344+
cygdrive
344345
CYHSCROLL
345346
CYMIN
346347
CYPADDEDBORDER

doc/cascadia/profiles.schema.json

+11
Original file line numberDiff line numberDiff line change
@@ -3104,6 +3104,17 @@
31043104
"default": false,
31053105
"description": "When set to true, the window will have an acrylic material background. When set to false, the window will have a plain, untextured background.",
31063106
"type": "boolean"
3107+
},
3108+
"pathTranslationStyle": {
3109+
"default": "none",
3110+
"description": "Controls how file paths are transformed when they are dragged and dropped on the terminal. Possible values are \"none\", \"wsl\", \"cygwin\" and \"msys2\".",
3111+
"enum": [
3112+
"none",
3113+
"wsl",
3114+
"cygwin",
3115+
"msys2"
3116+
],
3117+
"type": "string"
31073118
}
31083119
}
31093120
},

src/cascadia/TerminalControl/ControlInteractivity.cpp

-13
Original file line numberDiff line numberDiff line change
@@ -725,17 +725,4 @@ namespace winrt::Microsoft::Terminal::Control::implementation
725725
{
726726
return _core->GetRenderData();
727727
}
728-
729-
// Method Description:
730-
// - Used by the TermControl to know if it should translate drag-dropped
731-
// paths into WSL-friendly paths.
732-
// Arguments:
733-
// - <none>
734-
// Return Value:
735-
// - true if the connection we were created with was a WSL profile.
736-
bool ControlInteractivity::ManglePathsForWsl()
737-
{
738-
const auto source{ _core->Settings().ProfileSource() };
739-
return til::equals_insensitive_ascii(source, L"Windows.Terminal.Wsl") || til::equals_insensitive_ascii(source, L"Microsoft.WSL");
740-
}
741728
}

src/cascadia/TerminalControl/ControlInteractivity.h

-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
8686
const Windows::Foundation::IReference<CopyFormat>& formats);
8787
void RequestPasteTextFromClipboard();
8888
void SetEndSelectionPoint(const Core::Point pixelPosition);
89-
bool ManglePathsForWsl();
9089

9190
uint64_t Id();
9291
void AttachToNewControl(const Microsoft::Terminal::Control::IKeyBindings& keyBindings);

src/cascadia/TerminalControl/ControlInteractivity.idl

-2
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,6 @@ namespace Microsoft.Terminal.Control
6666

6767
void UpdateScrollbar(Single newValue);
6868

69-
Boolean ManglePathsForWsl { get; };
70-
7169
event Windows.Foundation.TypedEventHandler<Object, OpenHyperlinkEventArgs> OpenHyperlink;
7270
event Windows.Foundation.TypedEventHandler<Object, ScrollPositionChangedArgs> ScrollPositionChanged;
7371
event Windows.Foundation.TypedEventHandler<Object, PasteFromClipboardEventArgs> PasteFromClipboard;

src/cascadia/TerminalControl/IControlSettings.idl

+10-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ namespace Microsoft.Terminal.Control
2121
Aliased
2222
};
2323

24+
enum PathTranslationStyle
25+
{
26+
None = 0,
27+
WSL,
28+
Cygwin,
29+
MSYS2
30+
};
31+
2432
// Class Description:
2533
// TerminalSettings encapsulates all settings that control the
2634
// TermControl's behavior. In these settings there is both the entirety
@@ -30,7 +38,6 @@ namespace Microsoft.Terminal.Control
3038
Microsoft.Terminal.Control.IControlAppearance
3139
{
3240
String ProfileName;
33-
String ProfileSource;
3441

3542
Boolean EnableUnfocusedAcrylic { get; };
3643
Guid SessionId { get; };
@@ -69,6 +76,8 @@ namespace Microsoft.Terminal.Control
6976
Boolean RightClickContextMenu { get; };
7077
Boolean RepositionCursorWithMouse { get; };
7178

79+
PathTranslationStyle PathTranslationStyle { get; };
80+
7281
// NOTE! When adding something here, make sure to update ControlProperties.h too!
7382
};
7483
}

src/cascadia/TerminalControl/TermControl.cpp

+54-66
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,54 @@ static Microsoft::Console::TSF::Handle& GetTSFHandle()
5858

5959
namespace winrt::Microsoft::Terminal::Control::implementation
6060
{
61+
static void _translatePathInPlace(std::wstring& fullPath, PathTranslationStyle translationStyle)
62+
{
63+
static constexpr wil::zwstring_view s_pathPrefixes[] = {
64+
{},
65+
/* WSL */ L"/mnt/",
66+
/* Cygwin */ L"/cygdrive/",
67+
/* MSYS2 */ L"/",
68+
};
69+
70+
if (translationStyle == PathTranslationStyle::None)
71+
{
72+
return;
73+
}
74+
75+
// All of the other path translation modes current result in /-delimited paths
76+
std::replace(fullPath.begin(), fullPath.end(), L'\\', L'/');
77+
78+
if (fullPath.size() >= 2 && fullPath.at(1) == L':')
79+
{
80+
// C:/foo/bar -> Cc/foo/bar
81+
fullPath.at(1) = til::tolower_ascii(fullPath.at(0));
82+
// Cc/foo/bar -> [PREFIX]c/foo/bar
83+
fullPath.replace(0, 1, s_pathPrefixes[static_cast<int>(translationStyle)]);
84+
}
85+
else if (translationStyle == PathTranslationStyle::WSL)
86+
{
87+
// Stripping the UNC name and distribution prefix only applies to WSL.
88+
static constexpr std::wstring_view wslPathPrefixes[] = { L"//wsl.localhost/", L"//wsl$/" };
89+
for (auto prefix : wslPathPrefixes)
90+
{
91+
if (til::starts_with(fullPath, prefix))
92+
{
93+
if (const auto idx = fullPath.find(L'/', prefix.size()); idx != std::wstring::npos)
94+
{
95+
// //wsl.localhost/Ubuntu-18.04/foo/bar -> /foo/bar
96+
fullPath.erase(0, idx);
97+
}
98+
else
99+
{
100+
// //wsl.localhost/Ubuntu-18.04 -> /
101+
fullPath = L"/";
102+
}
103+
break;
104+
}
105+
}
106+
}
107+
}
108+
61109
TsfDataProvider::TsfDataProvider(TermControl* termControl) noexcept :
62110
_termControl{ termControl }
63111
{
@@ -3196,79 +3244,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation
31963244
allPathsString += L" ";
31973245
}
31983246

3199-
// Fix path for WSL
3200-
// In the fullness of time, we should likely plumb this up
3201-
// to the TerminalApp layer, and have it make the decision
3202-
// if this control should have its path mangled (and do the
3203-
// mangling), rather than exposing the source concept to the
3204-
// Control layer.
3205-
//
3206-
// However, it's likely that the control layer may need to
3207-
// know about the source anyways in the future, to support
3208-
// GH#3158
3209-
const auto isWSL = _interactivity.ManglePathsForWsl();
3210-
3211-
if (isWSL)
3212-
{
3213-
std::replace(fullPath.begin(), fullPath.end(), L'\\', L'/');
3214-
3215-
if (fullPath.size() >= 2 && fullPath.at(1) == L':')
3216-
{
3217-
// C:/foo/bar -> Cc/foo/bar
3218-
fullPath.at(1) = til::tolower_ascii(fullPath.at(0));
3219-
// Cc/foo/bar -> /mnt/c/foo/bar
3220-
fullPath.replace(0, 1, L"/mnt/");
3221-
}
3222-
else
3223-
{
3224-
static constexpr std::wstring_view wslPathPrefixes[] = { L"//wsl.localhost/", L"//wsl$/" };
3225-
for (auto prefix : wslPathPrefixes)
3226-
{
3227-
if (til::starts_with(fullPath, prefix))
3228-
{
3229-
if (const auto idx = fullPath.find(L'/', prefix.size()); idx != std::wstring::npos)
3230-
{
3231-
// //wsl.localhost/Ubuntu-18.04/foo/bar -> /foo/bar
3232-
fullPath.erase(0, idx);
3233-
}
3234-
else
3235-
{
3236-
// //wsl.localhost/Ubuntu-18.04 -> /
3237-
fullPath = L"/";
3238-
}
3239-
break;
3240-
}
3241-
}
3242-
}
3243-
}
3247+
const auto translationStyle{ _core.Settings().PathTranslationStyle() };
3248+
_translatePathInPlace(fullPath, translationStyle);
32443249

3245-
const auto quotesNeeded = isWSL || fullPath.find(L' ') != std::wstring::npos;
3246-
const auto quotesChar = isWSL ? L'\'' : L'"';
3250+
// All translated paths get quotes, and all strings spaces get quotes; all translated paths get single quotes
3251+
const auto quotesNeeded = translationStyle != PathTranslationStyle::None || fullPath.find(L' ') != std::wstring::npos;
3252+
const auto quotesChar = translationStyle != PathTranslationStyle::None ? L'\'' : L'"';
32473253

32483254
// Append fullPath and also wrap it in quotes if needed
32493255
if (quotesNeeded)
32503256
{
32513257
allPathsString.push_back(quotesChar);
32523258
}
3253-
if (isWSL)
3254-
{
3255-
// Fix quoted path for WSL
3256-
// Single quote is allowed on the Win32 subsystem and must be processed on WSL profiles.
3257-
// Note that we assume that all paths are quoted using a pair of single quotes for WSL.
3258-
std::wstring_view fullPathView{ fullPath };
3259-
size_t pos;
3260-
while ((pos = fullPathView.find(L'\'')) != std::wstring_view::npos)
3261-
{
3262-
allPathsString.append(fullPathView.begin(), fullPathView.begin() + pos);
3263-
allPathsString.append(L"'\\''");
3264-
fullPathView.remove_prefix(pos + 1);
3265-
}
3266-
allPathsString.append(fullPathView);
3267-
}
3268-
else
3269-
{
3270-
allPathsString.append(fullPath);
3271-
}
3259+
allPathsString.append(fullPath);
32723260
if (quotesNeeded)
32733261
{
32743262
allPathsString.push_back(quotesChar);

src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
3939
INITIALIZE_BINDABLE_ENUM_SETTING(AntiAliasingMode, TextAntialiasingMode, winrt::Microsoft::Terminal::Control::TextAntialiasingMode, L"Profile_AntialiasingMode", L"Content");
4040
INITIALIZE_BINDABLE_ENUM_SETTING_REVERSE_ORDER(CloseOnExitMode, CloseOnExitMode, winrt::Microsoft::Terminal::Settings::Model::CloseOnExitMode, L"Profile_CloseOnExit", L"Content");
4141
INITIALIZE_BINDABLE_ENUM_SETTING(ScrollState, ScrollbarState, winrt::Microsoft::Terminal::Control::ScrollbarState, L"Profile_ScrollbarVisibility", L"Content");
42+
INITIALIZE_BINDABLE_ENUM_SETTING(PathTranslationStyle, PathTranslationStyle, winrt::Microsoft::Terminal::Control::PathTranslationStyle, L"Profile_PathTranslationStyle", L"Content");
4243

4344
// Add a property changed handler to our own property changed event.
4445
// This propagates changes from the settings model to anybody listening to our
@@ -76,6 +77,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
7677
{
7778
_NotifyChanges(L"HideIcon");
7879
}
80+
else if (viewModelProperty == L"PathTranslationStyle")
81+
{
82+
_NotifyChanges(L"CurrentPathTranslationStyle");
83+
}
7984
});
8085

8186
// Do the same for the starting directory

src/cascadia/TerminalSettingsEditor/ProfileViewModel.h

+2
Original file line numberDiff line numberDiff line change
@@ -124,12 +124,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
124124
OBSERVABLE_PROJECTED_SETTING(_profile, AllowVtChecksumReport);
125125
OBSERVABLE_PROJECTED_SETTING(_profile, AllowVtClipboardWrite);
126126
OBSERVABLE_PROJECTED_SETTING(_profile, AnswerbackMessage);
127+
OBSERVABLE_PROJECTED_SETTING(_profile, PathTranslationStyle);
127128

128129
WINRT_PROPERTY(bool, IsBaseLayer, false);
129130
WINRT_PROPERTY(bool, FocusDeleteButton, false);
130131
GETSET_BINDABLE_ENUM_SETTING(AntiAliasingMode, Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode);
131132
GETSET_BINDABLE_ENUM_SETTING(CloseOnExitMode, Microsoft::Terminal::Settings::Model::CloseOnExitMode, CloseOnExit);
132133
GETSET_BINDABLE_ENUM_SETTING(ScrollState, Microsoft::Terminal::Control::ScrollbarState, ScrollState);
134+
GETSET_BINDABLE_ENUM_SETTING(PathTranslationStyle, Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle);
133135

134136
private:
135137
Model::Profile _profile;

src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl

+4
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ namespace Microsoft.Terminal.Settings.Editor
5858
IInspectable CurrentScrollState;
5959
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> ScrollStateList { get; };
6060

61+
IInspectable CurrentPathTranslationStyle;
62+
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> PathTranslationStyleList { get; };
63+
6164
Boolean CanDeleteProfile { get; };
6265
Boolean FocusDeleteButton;
6366
Boolean IsBaseLayer;
@@ -117,5 +120,6 @@ namespace Microsoft.Terminal.Settings.Editor
117120
OBSERVABLE_PROJECTED_PROFILE_SETTING(String, AnswerbackMessage);
118121
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, RainbowSuggestions);
119122
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, AllowVtClipboardWrite);
123+
OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Control.PathTranslationStyle, PathTranslationStyle);
120124
}
121125
}

src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml

+12
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,18 @@
156156
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
157157
</local:SettingContainer>
158158

159+
<!-- Path Translation -->
160+
<local:SettingContainer x:Uid="Profile_PathTranslationStyle"
161+
ClearSettingValue="{x:Bind Profile.ClearPathTranslationStyle}"
162+
HasSettingValue="{x:Bind Profile.HasPathTranslationStyle, Mode=OneWay}"
163+
SettingOverrideSource="{x:Bind Profile.PathTranslationStyleOverrideSource, Mode=OneWay}">
164+
<ComboBox AutomationProperties.AccessibilityView="Content"
165+
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
166+
ItemsSource="{x:Bind Profile.PathTranslationStyleList, Mode=OneWay}"
167+
SelectedItem="{x:Bind Profile.CurrentPathTranslationStyle, Mode=TwoWay}"
168+
Style="{StaticResource ComboBoxSettingStyle}" />
169+
</local:SettingContainer>
170+
159171
</StackPanel>
160172
</Grid>
161173
</Page>

src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw

+40
Original file line numberDiff line numberDiff line change
@@ -1900,4 +1900,44 @@
19001900
<value>Useful for troubleshooting or developing Terminal.</value>
19011901
<comment>Additional description for what the "debug features enabled" setting does. Presented near "Globals_DebugFeaturesEnabled.Header".</comment>
19021902
</data>
1903+
<data name="Profile_PathTranslationStyle.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
1904+
<value>Path translation</value>
1905+
<comment>Name for a control to select how file and directory paths are translated.</comment>
1906+
</data>
1907+
<data name="Profile_PathTranslationStyle.Header" xml:space="preserve">
1908+
<value>Path translation</value>
1909+
<comment>Name for a control to select how file and directory paths are translated.</comment>
1910+
</data>
1911+
<data name="Profile_PathTranslationStyle.HelpText" xml:space="preserve">
1912+
<value>Controls how file and directory paths are translated during drag-and-drop operations.</value>
1913+
<comment>A description for what the "path translation" setting does. Presented near "Profile_PathTranslationStyle.Header".</comment>
1914+
</data>
1915+
<data name="Profile_PathTranslationStyleNone.Content" xml:space="preserve">
1916+
<value>None</value>
1917+
<comment>An option to choose from for the "path translation" setting.</comment>
1918+
</data>
1919+
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
1920+
<value>WSL (C:\ -> /mnt/c)</value>
1921+
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
1922+
</data>
1923+
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
1924+
<value>Cygwin (C:\ -> /cygdrive/c)</value>
1925+
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
1926+
</data>
1927+
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
1928+
<value>MSYS2 (C:\ -> /c)</value>
1929+
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
1930+
</data>
1931+
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
1932+
<value>Profile no longer detected</value>
1933+
</data>
1934+
<data name="Profile_Delete_Orphaned.HelpText" xml:space="preserve">
1935+
<value>This automatically-detected profile appears to have been uninstalled. Changes you have made to it are preserved, but it cannot be used until it has been reinstalled.</value>
1936+
</data>
1937+
<data name="Profile_Source_Orphaned.Header" xml:space="preserve">
1938+
<value>Original Source</value>
1939+
</data>
1940+
<data name="Profile_Source_Orphaned.HelpText" xml:space="preserve">
1941+
<value>Indicates the software that originally created this profile.</value>
1942+
</data>
19031943
</root>

src/cascadia/TerminalSettingsModel/EnumMappings.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
5151
DEFINE_ENUM_MAP(Microsoft::Terminal::Core::CursorStyle, CursorStyle);
5252
DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::IntenseStyle, IntenseTextStyle);
5353
DEFINE_ENUM_MAP(Microsoft::Terminal::Core::AdjustTextMode, AdjustIndistinguishableColors);
54+
DEFINE_ENUM_MAP(Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle);
5455

5556
// FontWeight is special because the JsonUtils::ConversionTrait for it
5657
// creates a FontWeight object, but we need to use the uint16_t value.

src/cascadia/TerminalSettingsModel/EnumMappings.h

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
4848
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, uint16_t> FontWeight();
4949
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Settings::Model::IntenseStyle> IntenseTextStyle();
5050
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Core::AdjustTextMode> AdjustIndistinguishableColors();
51+
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Control::PathTranslationStyle> PathTranslationStyle();
5152
};
5253
}
5354

src/cascadia/TerminalSettingsModel/EnumMappings.idl

+1
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,6 @@ namespace Microsoft.Terminal.Settings.Model
3030
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Core.AdjustTextMode> AdjustIndistinguishableColors { get; };
3131
static Windows.Foundation.Collections.IMap<String, UInt16> FontWeight { get; };
3232
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.IntenseStyle> IntenseTextStyle { get; };
33+
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Control.PathTranslationStyle> PathTranslationStyle { get; };
3334
}
3435
}

0 commit comments

Comments
 (0)