Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/cascadia/TerminalApp/AppActionHandlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1475,7 +1475,7 @@ namespace winrt::TerminalApp::implementation
WI_IsAnyFlagSet(source, SuggestionsSource::CommandHistory | SuggestionsSource::QuickFixes);
if (const auto& control{ _GetActiveControl() })
{
currentWorkingDirectory = control.CurrentWorkingDirectory();
currentWorkingDirectory = control.WorkingDirectory();

if (shouldGetContext)
{
Expand Down
6 changes: 2 additions & 4 deletions src/cascadia/TerminalApp/TerminalPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1600,8 +1600,7 @@ namespace winrt::TerminalApp::implementation

// Replace the Starting directory with the CWD, if given
const auto workingDirectory = control.WorkingDirectory();
const auto validWorkingDirectory = !workingDirectory.empty();
if (validWorkingDirectory)
if (Utils::IsValidDirectory(workingDirectory.c_str()))
{
controlSettings.DefaultSettings()->StartingDirectory(workingDirectory);
}
Expand Down Expand Up @@ -3555,8 +3554,7 @@ namespace winrt::TerminalApp::implementation
profile = GetClosestProfileForDuplicationOfProfile(profile);
controlSettings = Settings::TerminalSettings::CreateWithProfile(_settings, profile);
const auto workingDirectory = tabImpl->GetActiveTerminalControl().WorkingDirectory();
const auto validWorkingDirectory = !workingDirectory.empty();
if (validWorkingDirectory)
if (Utils::IsValidDirectory(workingDirectory.c_str()))
{
controlSettings.DefaultSettings()->StartingDirectory(workingDirectory);
}
Expand Down
2 changes: 1 addition & 1 deletion src/cascadia/TerminalApp/TerminalPaneContent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ namespace winrt::TerminalApp::implementation

args.Profile(::Microsoft::Console::Utils::GuidToString(_profile.Guid()));
// If we know the user's working directory use it instead of the profile.
if (const auto dir = _control.WorkingDirectory(); !dir.empty())
if (const auto dir = _control.WorkingDirectory(); ::Microsoft::Console::Utils::IsValidDirectory(dir.c_str()))
{
args.StartingDirectory(dir);
}
Expand Down
5 changes: 0 additions & 5 deletions src/cascadia/TerminalControl/ControlCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2420,11 +2420,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return *context;
}

winrt::hstring ControlCore::CurrentWorkingDirectory() const
{
return winrt::hstring{ _terminal->GetWorkingDirectory() };
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i s2g, we just had a whole-ass duplicate of this function?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah lol. I was super surprised about that too. We don't have skeletons in our basement, we got entire graveyards apparently lmao.

}

bool ControlCore::QuickFixesAvailable() const noexcept
{
return _cachedQuickFixes && _cachedQuickFixes.Size() > 0;
Expand Down
2 changes: 0 additions & 2 deletions src/cascadia/TerminalControl/ControlCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation

void ContextMenuSelectCommand();
void ContextMenuSelectOutput();

winrt::hstring CurrentWorkingDirectory() const;
#pragma endregion

#pragma region ITerminalInput
Expand Down
2 changes: 0 additions & 2 deletions src/cascadia/TerminalControl/ICoreState.idl
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,5 @@ namespace Microsoft.Terminal.Control
void SelectOutput(Boolean goUp);
IVector<ScrollMark> ScrollMarks { get; };

String CurrentWorkingDirectory { get; };

};
}
4 changes: 0 additions & 4 deletions src/cascadia/TerminalControl/TermControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3713,10 +3713,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
return _core.CommandHistory();
}
winrt::hstring TermControl::CurrentWorkingDirectory() const
{
return _core.CurrentWorkingDirectory();
}

void TermControl::UpdateWinGetSuggestions(Windows::Foundation::Collections::IVector<hstring> suggestions)
{
Expand Down
2 changes: 0 additions & 2 deletions src/cascadia/TerminalControl/TermControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ScrollToMark(const Control::ScrollToMarkDirection& direction);
void SelectCommand(const bool goUp);
void SelectOutput(const bool goUp);

winrt::hstring CurrentWorkingDirectory() const;
#pragma endregion

void ScrollViewport(int viewTop);
Expand Down
5 changes: 1 addition & 4 deletions src/terminal/adapter/ITermDispatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch
virtual void BackIndex() = 0; // DECBI
virtual void ForwardIndex() = 0; // DECFI
virtual void SetWindowTitle(std::wstring_view title) = 0; // DECSWT, OscWindowTitle
virtual void SetCurrentWorkingDirectory(const std::wstring_view uri) = 0; // OSC 7
virtual void HorizontalTabSet() = 0; // HTS
virtual void ForwardTab(const VTInt numTabs) = 0; // CHT, HT
virtual void BackwardsTab(const VTInt numTabs) = 0; // CBT
Expand Down Expand Up @@ -156,13 +157,9 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch
virtual void EndHyperlink() = 0;

virtual void DoConEmuAction(const std::wstring_view string) = 0;

virtual void DoITerm2Action(const std::wstring_view string) = 0;

virtual void DoFinalTermAction(const std::wstring_view string) = 0;

virtual void DoVsCodeAction(const std::wstring_view string) = 0;

virtual void DoWTAction(const std::wstring_view string) = 0;

virtual StringHandler DefineSixelImage(const VTInt macroParameter,
Expand Down
21 changes: 21 additions & 0 deletions src/terminal/adapter/adaptDispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2615,6 +2615,27 @@ void AdaptDispatch::SetWindowTitle(std::wstring_view title)
_api.SetWindowTitle(title);
}

// OSC 7 - Set Current Working Directory
// While ConEmu's OSC 9;9 works well for native Windows paths,
// OSC 7 uses file URIs, which may not always work.
void AdaptDispatch::SetCurrentWorkingDirectory(std::wstring_view uri)
{
// Ensure that the URI has a null terminator.
std::wstring path{ uri };

// PathCreateFromUrlW supports writing to the input pointer,
// and the resulting path can never be longer than the URI.
const auto ptr = path.data();
auto len = gsl::narrow<DWORD>(path.size());
THROW_IF_FAILED(PathCreateFromUrlW(ptr, ptr, &len, 0));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will add a dependency from conhost on the library containing PathCreateFromUrlW; is that acceptable?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.... it's in kernelbase?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

    api-ms-win-core-url-l1-1-0
      PathCreateFromUrlW

hmm where are you hosted little guy

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

> $d.Apis["PathCreateFromUrlW"][1].Binary


Architecture                       : Amd64
BinplacePath                       : kernelbase.dll

wellp

path.resize(len);

if (til::is_legal_path(path))
{
_api.SetWorkingDirectory(path);
}
}

//Routine Description:
// HTS - sets a VT tab stop in the cursor's current column.
//Arguments:
Expand Down
1 change: 1 addition & 0 deletions src/terminal/adapter/adaptDispatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ namespace Microsoft::Console::VirtualTerminal
void BackIndex() override; // DECBI
void ForwardIndex() override; // DECFI
void SetWindowTitle(const std::wstring_view title) override; // DECSWT, OSCWindowTitle
void SetCurrentWorkingDirectory(std::wstring_view uri) override; // OSC 7
void HorizontalTabSet() override; // HTS
void ForwardTab(const VTInt numTabs) override; // CHT, HT
void BackwardsTab(const VTInt numTabs) override; // CBT
Expand Down
3 changes: 2 additions & 1 deletion src/terminal/adapter/precomp.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ Module Name:
// This includes support libraries from the CRT, STL, WIL, and GSL
#include "LibraryIncludes.h"

#include <cmath>
#include <Shlwapi.h>

#define ENABLE_INTSAFE_SIGNED_FUNCTIONS
#include <intsafe.h>

Expand Down
1 change: 1 addition & 0 deletions src/terminal/adapter/termDispatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class Microsoft::Console::VirtualTerminal::TermDispatch : public Microsoft::Cons
void BackIndex() override {} // DECBI
void ForwardIndex() override {} // DECFI
void SetWindowTitle(std::wstring_view /*title*/) override {} // DECSWT, OscWindowTitle
void SetCurrentWorkingDirectory(std::wstring_view /*uri*/) override {} // OSC 7
void HorizontalTabSet() override {} // HTS
void ForwardTab(const VTInt /*numTabs*/) override {} // CHT, HT
void BackwardsTab(const VTInt /*numTabs*/) override {} // CBT
Expand Down
3 changes: 1 addition & 2 deletions src/terminal/parser/OutputStateMachineEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -865,8 +865,7 @@ bool OutputStateMachineEngine::ActionOscDispatch(const size_t parameter, const s
break;
}
case OscActionCodes::CurrentWorkingDirectory:
// TODO: Add support for OSC 7 = CWD sequences?
// In GH#8214 it was decided that it's a bad idea due to WSL compat.
_dispatch->SetCurrentWorkingDirectory(string);
break;
case OscActionCodes::Hyperlink:
{
Expand Down
2 changes: 2 additions & 0 deletions src/types/inc/utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ namespace Microsoft::Console::Utils

const wchar_t* FindActionableControlCharacter(const wchar_t* beg, const size_t len) noexcept;

bool IsValidDirectory(const wchar_t* path) noexcept;

// Same deal, but in TerminalPage::_evaluatePathForCwd
std::wstring EvaluateStartingDirectory(std::wstring_view cwd, std::wstring_view startingDirectory);

Expand Down
13 changes: 13 additions & 0 deletions src/types/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1237,6 +1237,19 @@ const wchar_t* Utils::FindActionableControlCharacter(const wchar_t* beg, const s
return it;
}

// Returns true if it's a valid path to a directory.
bool Utils::IsValidDirectory(const wchar_t* path) noexcept
{
if (path == nullptr || *path == L'\0')
{
return false;
}

WIN32_FILE_ATTRIBUTE_DATA data;
const auto ok = GetFileAttributesExW(path, GetFileExInfoStandard, &data);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of std::filesystem::access or whatever? cool w/ me

Copy link
Copy Markdown
Member Author

@lhecker lhecker Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I just hate that the std::filesystem::path objects are expensive to construct. Under the hood, it gets passed to __std_fs_get_stats, which - and you will never guess this - takes a null-terminated string. 🤦‍♂️

Hurr durr "C++ is for low level zero overhead real man's man engineering."
Meanwhile: Wrap in wstring to unwrap in pointer. lmao

return ok && (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
}

#pragma warning(pop)

std::wstring Utils::EvaluateStartingDirectory(
Expand Down
Loading