Skip to content

Commit 0689067

Browse files
authored
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.
1 parent 90866c7 commit 0689067

22 files changed

+150
-68
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
@@ -3095,6 +3095,17 @@
30953095
"default": false,
30963096
"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.",
30973097
"type": "boolean"
3098+
},
3099+
"pathTranslationStyle": {
3100+
"default": "none",
3101+
"description": "Controls how file paths are transformed when they are dragged and dropped on the terminal. Possible values are \"none\", \"wsl\", \"cygwin\" and \"msys2\".",
3102+
"enum": [
3103+
"none",
3104+
"wsl",
3105+
"cygwin",
3106+
"msys2"
3107+
],
3108+
"type": "string"
30983109
}
30993110
}
31003111
},

src/cascadia/TerminalControl/ControlInteractivity.cpp

-12
Original file line numberDiff line numberDiff line change
@@ -725,16 +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-
return _core->Settings().ProfileSource() == L"Windows.Terminal.Wsl";
739-
}
740728
}

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

+53-47
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
{
@@ -3215,54 +3263,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
32153263
allPathsString += L" ";
32163264
}
32173265

3218-
// Fix path for WSL
3219-
// In the fullness of time, we should likely plumb this up
3220-
// to the TerminalApp layer, and have it make the decision
3221-
// if this control should have its path mangled (and do the
3222-
// mangling), rather than exposing the source concept to the
3223-
// Control layer.
3224-
//
3225-
// However, it's likely that the control layer may need to
3226-
// know about the source anyways in the future, to support
3227-
// GH#3158
3228-
const auto isWSL = _interactivity.ManglePathsForWsl();
3229-
3230-
if (isWSL)
3231-
{
3232-
std::replace(fullPath.begin(), fullPath.end(), L'\\', L'/');
3233-
3234-
if (fullPath.size() >= 2 && fullPath.at(1) == L':')
3235-
{
3236-
// C:/foo/bar -> Cc/foo/bar
3237-
fullPath.at(1) = til::tolower_ascii(fullPath.at(0));
3238-
// Cc/foo/bar -> /mnt/c/foo/bar
3239-
fullPath.replace(0, 1, L"/mnt/");
3240-
}
3241-
else
3242-
{
3243-
static constexpr std::wstring_view wslPathPrefixes[] = { L"//wsl.localhost/", L"//wsl$/" };
3244-
for (auto prefix : wslPathPrefixes)
3245-
{
3246-
if (til::starts_with(fullPath, prefix))
3247-
{
3248-
if (const auto idx = fullPath.find(L'/', prefix.size()); idx != std::wstring::npos)
3249-
{
3250-
// //wsl.localhost/Ubuntu-18.04/foo/bar -> /foo/bar
3251-
fullPath.erase(0, idx);
3252-
}
3253-
else
3254-
{
3255-
// //wsl.localhost/Ubuntu-18.04 -> /
3256-
fullPath = L"/";
3257-
}
3258-
break;
3259-
}
3260-
}
3261-
}
3262-
}
3266+
const auto translationStyle{ _core.Settings().PathTranslationStyle() };
3267+
_translatePathInPlace(fullPath, translationStyle);
32633268

3264-
const auto quotesNeeded = isWSL || fullPath.find(L' ') != std::wstring::npos;
3265-
const auto quotesChar = isWSL ? L'\'' : L'"';
3269+
// All translated paths get quotes, and all strings spaces get quotes; all translated paths get single quotes
3270+
const auto quotesNeeded = translationStyle != PathTranslationStyle::None || fullPath.find(L' ') != std::wstring::npos;
3271+
const auto quotesChar = translationStyle != PathTranslationStyle::None ? L'\'' : L'"';
32663272

32673273
// Append fullPath and also wrap it in quotes if needed
32683274
if (quotesNeeded)

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
@@ -126,12 +126,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
126126
OBSERVABLE_PROJECTED_SETTING(_profile, AllowVtChecksumReport);
127127
OBSERVABLE_PROJECTED_SETTING(_profile, AnswerbackMessage);
128128
OBSERVABLE_PROJECTED_SETTING(_profile, RainbowSuggestions);
129+
OBSERVABLE_PROJECTED_SETTING(_profile, PathTranslationStyle);
129130

130131
WINRT_PROPERTY(bool, IsBaseLayer, false);
131132
WINRT_PROPERTY(bool, FocusDeleteButton, false);
132133
GETSET_BINDABLE_ENUM_SETTING(AntiAliasingMode, Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode);
133134
GETSET_BINDABLE_ENUM_SETTING(CloseOnExitMode, Microsoft::Terminal::Settings::Model::CloseOnExitMode, CloseOnExit);
134135
GETSET_BINDABLE_ENUM_SETTING(ScrollState, Microsoft::Terminal::Control::ScrollbarState, ScrollState);
136+
GETSET_BINDABLE_ENUM_SETTING(PathTranslationStyle, Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle);
135137

136138
private:
137139
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(Boolean, AllowVtChecksumReport);
118121
OBSERVABLE_PROJECTED_PROFILE_SETTING(String, AnswerbackMessage);
119122
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, RainbowSuggestions);
123+
OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Control.PathTranslationStyle, PathTranslationStyle);
120124
}
121125
}

src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml

+13
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,19 @@
164164
<ToggleSwitch IsOn="{x:Bind Profile.RainbowSuggestions, Mode=TwoWay}"
165165
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
166166
</local:SettingContainer>
167+
168+
<!-- Path Translation -->
169+
<local:SettingContainer x:Uid="Profile_PathTranslationStyle"
170+
ClearSettingValue="{x:Bind Profile.ClearPathTranslationStyle}"
171+
HasSettingValue="{x:Bind Profile.HasPathTranslationStyle, Mode=OneWay}"
172+
SettingOverrideSource="{x:Bind Profile.PathTranslationStyleOverrideSource, Mode=OneWay}">
173+
<ComboBox AutomationProperties.AccessibilityView="Content"
174+
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
175+
ItemsSource="{x:Bind Profile.PathTranslationStyleList, Mode=OneWay}"
176+
SelectedItem="{x:Bind Profile.CurrentPathTranslationStyle, Mode=TwoWay}"
177+
Style="{StaticResource ComboBoxSettingStyle}" />
178+
</local:SettingContainer>
179+
167180
</StackPanel>
168181
</Grid>
169182
</Page>

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

+28
Original file line numberDiff line numberDiff line change
@@ -1961,6 +1961,34 @@
19611961
<value>Display a shield in the title bar when Windows Terminal is running as Administrator</value>
19621962
<comment>Header for a control to toggle displaying a shield in the title bar of the app. "Admin" refers to elevated sessions like "run as Admin"</comment>
19631963
</data>
1964+
<data name="Profile_PathTranslationStyle.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
1965+
<value>Path translation</value>
1966+
<comment>Name for a control to select how file and directory paths are translated.</comment>
1967+
</data>
1968+
<data name="Profile_PathTranslationStyle.Header" xml:space="preserve">
1969+
<value>Path translation</value>
1970+
<comment>Name for a control to select how file and directory paths are translated.</comment>
1971+
</data>
1972+
<data name="Profile_PathTranslationStyle.HelpText" xml:space="preserve">
1973+
<value>Controls how file and directory paths are translated during drag-and-drop operations.</value>
1974+
<comment>A description for what the "path translation" setting does. Presented near "Profile_PathTranslationStyle.Header".</comment>
1975+
</data>
1976+
<data name="Profile_PathTranslationStyleNone.Content" xml:space="preserve">
1977+
<value>None</value>
1978+
<comment>An option to choose from for the "path translation" setting.</comment>
1979+
</data>
1980+
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
1981+
<value>WSL (C:\ -> /mnt/c)</value>
1982+
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
1983+
</data>
1984+
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
1985+
<value>Cygwin (C:\ -> /cygdrive/c)</value>
1986+
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
1987+
</data>
1988+
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
1989+
<value>MSYS2 (C:\ -> /c)</value>
1990+
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
1991+
</data>
19641992
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
19651993
<value>Profile no longer detected</value>
19661994
</data>

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
}

src/cascadia/TerminalSettingsModel/MTSMSettings.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ Author(s):
102102
X(bool, RainbowSuggestions, "experimental.rainbowSuggestions", false) \
103103
X(bool, ForceVTInput, "compatibility.input.forceVT", false) \
104104
X(bool, AllowVtChecksumReport, "compatibility.allowDECRQCRA", false) \
105-
X(bool, AllowKeypadMode, "compatibility.allowDECNKM", false)
105+
X(bool, AllowKeypadMode, "compatibility.allowDECNKM", false) \
106+
X(Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, "pathTranslationStyle", Microsoft::Terminal::Control::PathTranslationStyle::None)
106107

107108
// Intentionally omitted Profile settings:
108109
// * Name

src/cascadia/TerminalSettingsModel/Profile.idl

+2
Original file line numberDiff line numberDiff line change
@@ -94,5 +94,7 @@ namespace Microsoft.Terminal.Settings.Model
9494
INHERITABLE_PROFILE_SETTING(Boolean, ForceVTInput);
9595
INHERITABLE_PROFILE_SETTING(Boolean, AllowVtChecksumReport);
9696
INHERITABLE_PROFILE_SETTING(Boolean, AllowKeypadMode);
97+
98+
INHERITABLE_PROFILE_SETTING(Microsoft.Terminal.Control.PathTranslationStyle, PathTranslationStyle);
9799
}
98100
}

src/cascadia/TerminalSettingsModel/TerminalSettings.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
288288

289289
// Fill in the remaining properties from the profile
290290
_ProfileName = profile.Name();
291-
_ProfileSource = profile.Source();
292291

293292
const auto fontInfo = profile.FontInfo();
294293
_FontFace = fontInfo.FontFace();
@@ -349,6 +348,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
349348
_RainbowSuggestions = profile.RainbowSuggestions();
350349
_ForceVTInput = profile.ForceVTInput();
351350
_AllowVtChecksumReport = profile.AllowVtChecksumReport();
351+
_PathTranslationStyle = profile.PathTranslationStyle();
352352
}
353353

354354
// Method Description:

0 commit comments

Comments
 (0)