Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for colon separated sub-parameters #15648

Merged
merged 32 commits into from
Jul 18, 2023
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
91493b5
add support for colon seperated sub-parameters
tusharsnx Jul 3, 2023
1ed4ba2
use gsl::narrow instead of static_cast
tusharsnx Jul 3, 2023
84735d9
add tests
tusharsnx Jul 4, 2023
b958427
runformat
tusharsnx Jul 4, 2023
470e163
first sub param is also followed by colon(:)
tusharsnx Jul 4, 2023
05b6bf6
rename sub parameter ranges
tusharsnx Jul 6, 2023
4180465
fix subspan creation
tusharsnx Jul 6, 2023
ce9ce2f
invalidate sequences with extra sub parameters
tusharsnx Jul 7, 2023
94e9156
fix typo
tusharsnx Jul 7, 2023
e6bbfbb
correct spell check
tusharsnx Jul 7, 2023
0c0da72
fix the check for hasSubParamsFor
tusharsnx Jul 8, 2023
7869d85
skip parameter with unrecognised sub parameters
tusharsnx Jul 9, 2023
ee58a9e
discard parameter when we can't handle its sub parameter(s)
tusharsnx Jul 9, 2023
4d3049a
fix small error message while we are at it
tusharsnx Jul 9, 2023
c5334c7
handle DECCARA and DECRARA sequences with sub parameters
tusharsnx Jul 9, 2023
e17c1cf
start ignoring (sub)parameters once we overflow the limit
tusharsnx Jul 10, 2023
a67ffbb
format
tusharsnx Jul 10, 2023
0c86064
remove last few traces of CsiParamIgnore because we don't need it
tusharsnx Jul 10, 2023
0778eea
remove few words, seemed unneccesary
tusharsnx Jul 10, 2023
1d83b80
update TestCsiMaxSubParamCount
tusharsnx Jul 10, 2023
cc59dde
fix typo in OutputEngineTest
tusharsnx Jul 10, 2023
95067c8
remove outdated test cases
tusharsnx Jul 10, 2023
ecad2af
refactor _ApplyGraphicOption so DECRARA can support sub params
tusharsnx Jul 11, 2023
16cb424
store upto MAX_SUBPARAMETER_COUNT sub params per parameter
tusharsnx Jul 11, 2023
483ec2b
update test final
tusharsnx Jul 11, 2023
f5177a0
use correct flag
tusharsnx Jul 11, 2023
f135dfc
set flag once limit is overflowed
tusharsnx Jul 12, 2023
9630376
refactor test
tusharsnx Jul 12, 2023
86f128d
add VTSubParameters class
tusharsnx Jul 12, 2023
2fdc975
format
tusharsnx Jul 12, 2023
edecf0a
remove unused GraphicOptionsWithSubParams
tusharsnx Jul 13, 2023
adaa841
use narrow_cast
tusharsnx Jul 13, 2023
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
125 changes: 112 additions & 13 deletions src/terminal/adapter/DispatchTypes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,67 +152,157 @@ namespace Microsoft::Console::VirtualTerminal
VTInt _value;
};

class VTSubParameters
{
public:
constexpr VTSubParameters() noexcept
{
}

constexpr VTSubParameters(const std::span<const VTParameter> subParams) noexcept :
_subParams{ subParams }
{
}

constexpr VTParameter at(const size_t index) const noexcept
{
return til::at(_subParams, index);
}

VTSubParameters subspan(const size_t offset, const size_t count) const noexcept
{
const auto subParamsSpan = _subParams.subspan(offset, count);
return { subParamsSpan };
}

bool empty() const noexcept
{
return _subParams.empty();
}

size_t size() const noexcept
{
return _subParams.size();
}

constexpr operator std::span<const VTParameter>() const noexcept
{
return _subParams;
}

private:
std::span<const VTParameter> _subParams;
};

class VTParameters
{
public:
constexpr VTParameters() noexcept
{
}

constexpr VTParameters(const VTParameter* ptr, const size_t count) noexcept :
_values{ ptr, count }
constexpr VTParameters(const VTParameter* paramsPtr, const size_t paramsCount) noexcept :
_params{ paramsPtr, paramsCount },
_subParams{},
_subParamRanges{}
{
}

constexpr VTParameters(const std::span<const VTParameter> params,
const std::span<const VTParameter> subParams,
const std::span<const std::pair<BYTE, BYTE>> subParamRanges) noexcept :
_params{ params },
_subParams{ subParams },
_subParamRanges{ subParamRanges }
{
}

constexpr VTParameter at(const size_t index) const noexcept
{
// If the index is out of range, we return a parameter with no value.
return index < _values.size() ? til::at(_values, index) : defaultParameter;
return index < _params.size() ? til::at(_params, index) : defaultParameter;
}

constexpr bool empty() const noexcept
{
return _values.empty();
return _params.empty();
}

constexpr size_t size() const noexcept
{
// We always return a size of at least 1, since an empty parameter
// list is the equivalent of a single "default" parameter.
return std::max<size_t>(_values.size(), 1);
return std::max<size_t>(_params.size(), 1);
}

VTParameters subspan(const size_t offset) const noexcept
{
const auto subValues = _values.subspan(std::min(offset, _values.size()));
return { subValues.data(), subValues.size() };
// We need sub parameters to always be in their original index
// because we store their indexes in subParamRanges. So we pass
// _subParams as is and create new span for others.
const auto newParamsSpan = _params.subspan(std::min(offset, _params.size()));
const auto newSubParamRangesSpan = _subParamRanges.subspan(std::min(offset, _subParamRanges.size()));
return { newParamsSpan, _subParams, newSubParamRangesSpan };
DHowett marked this conversation as resolved.
Show resolved Hide resolved
}

VTSubParameters subParamsFor(const size_t index) const noexcept
{
if (index < _subParamRanges.size())
{
const auto& range = til::at(_subParamRanges, index);
return _subParams.subspan(range.first, range.second - range.first);
}
else
{
return VTSubParameters{};
}
}

bool hasSubParams() const noexcept
{
return !_subParams.empty();
}

bool hasSubParamsFor(const size_t index) const noexcept
{
if (index < _subParamRanges.size())
{
const auto& range = til::at(_subParamRanges, index);
return range.second > range.first;
}
else
{
return false;
}
}

template<typename T>
bool for_each(const T&& predicate) const
{
auto values = _values;
auto params = _params;

// We always return at least 1 value here, since an empty parameter
// list is the equivalent of a single "default" parameter.
if (values.empty())
if (params.empty())
{
values = defaultParameters;
params = defaultParameters;
}

auto success = true;
for (const auto& v : values)
for (const auto& v : params)
{
success = predicate(v) && success;
}
return success;
}

private:
static constexpr VTParameter defaultParameter;
static constexpr VTParameter defaultParameter{};
static constexpr std::span defaultParameters{ &defaultParameter, 1 };

std::span<const VTParameter> _values;
std::span<const VTParameter> _params;
VTSubParameters _subParams;
std::span<const std::pair<BYTE, BYTE>> _subParamRanges;
};

// FlaggedEnumValue is a convenience class that produces enum values (of a specified size)
Expand Down Expand Up @@ -359,6 +449,15 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes
BrightBackgroundWhite = 107,
};

// we use this in VT parser to indicate that the GraphicsOptions (parameter)
// accepts sub-parameters.
enum GraphicsOptionsWithSubParams : VTInt
{
#ifdef UNIT_TESTING
TestOption = 12345,
#endif
};

tusharsnx marked this conversation as resolved.
Show resolved Hide resolved
enum LogicalAttributeOptions : VTInt
{
Default = 0,
Expand Down
3 changes: 3 additions & 0 deletions src/terminal/adapter/adaptDispatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,9 @@ namespace Microsoft::Console::VirtualTerminal
size_t _ApplyGraphicsOption(const VTParameters options,
const size_t optionIndex,
TextAttribute& attr) noexcept;
void _ApplyGraphicsOptionSubParam(const VTParameter option,
const VTSubParameters subParams,
TextAttribute& attr) noexcept;
void _ApplyGraphicsOptions(const VTParameters options,
TextAttribute& attr) noexcept;

Expand Down
26 changes: 26 additions & 0 deletions src/terminal/adapter/adaptDispatchGraphics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ size_t AdaptDispatch::_SetRgbColorsHelper(const VTParameters options,

// Routine Description:
// - Helper to apply a single graphic rendition option to an attribute.
// - Calls appropriate helper to apply the option with sub parameters when necessary.
// Arguments:
// - options - An array of options.
// - optionIndex - The start index of the option that will be applied.
Expand All @@ -75,6 +76,14 @@ size_t AdaptDispatch::_ApplyGraphicsOption(const VTParameters options,
TextAttribute& attr) noexcept
{
const GraphicsOptions opt = options.at(optionIndex);

if (options.hasSubParamsFor(optionIndex))
{
const auto subParams = options.subParamsFor(optionIndex);
_ApplyGraphicsOptionSubParam(opt, subParams, attr);
return 1;
}

switch (opt)
{
case Off:
Expand Down Expand Up @@ -250,6 +259,23 @@ size_t AdaptDispatch::_ApplyGraphicsOption(const VTParameters options,
}
}

// Routine Description:
// - This is a no-op until we have something meaningful to do with sub parameters.
// - Helper to apply a single graphic rendition option with sub parameters to an attribute.
// Arguments:
// - option - An option to apply.
// - attr - The attribute that will be updated with the applied option.
// Return Value:
// - <None>
void AdaptDispatch::_ApplyGraphicsOptionSubParam(const VTParameter /* option */,
const VTSubParameters /* subParam */,
TextAttribute& /* attr */) noexcept
{
// here, we apply our "best effort" rule, while handling sub params if we don't
// recognise the parameter substring (parameter and it's sub parameters) then
// we should just skip over them.
}

// Routine Description:
// - Helper to apply a number of graphic rendition options to an attribute.
// Arguments:
Expand Down
26 changes: 26 additions & 0 deletions src/terminal/parser/OutputStateMachineEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,12 @@ bool OutputStateMachineEngine::ActionVt52EscDispatch(const VTID id, const VTPara
// - true iff we successfully dispatched the sequence.
bool OutputStateMachineEngine::ActionCsiDispatch(const VTID id, const VTParameters parameters)
{
// Bail out if we receive subparameters, but we don't accept them in the sequence.
if (parameters.hasSubParams() && !_CanSeqAcceptSubParam(id, parameters)) [[unlikely]]
{
return false;
}

auto success = false;

switch (id)
Expand Down Expand Up @@ -1098,6 +1104,26 @@ bool OutputStateMachineEngine::_GetOscSetClipboard(const std::wstring_view strin
return SUCCEEDED_LOG(Base64::Decode(substr, content));
}

// Routine Description:
// - Takes a sequence id ("final byte") and determines if it accepts sub parameters.
// Arguments:
// - id - The sequence id to check for.
// Return Value:
// - True, if it accepts sub parameters or else False.
bool OutputStateMachineEngine::_CanSeqAcceptSubParam(const VTID id, const VTParameters& parameters) noexcept
{
switch (id)
{
case SGR_SetGraphicsRendition:
return true;
case DECCARA_ChangeAttributesRectangularArea:
case DECRARA_ReverseAttributesRectangularArea:
return !parameters.hasSubParamsFor(0) && !parameters.hasSubParamsFor(1) && !parameters.hasSubParamsFor(2) && !parameters.hasSubParamsFor(3);
DHowett marked this conversation as resolved.
Show resolved Hide resolved
default:
return false;
}
}

// Method Description:
// - Clears our last stored character. The last stored character is the last
// graphical character we printed, which is reset if any other action is
Expand Down
2 changes: 2 additions & 0 deletions src/terminal/parser/OutputStateMachineEngine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@ namespace Microsoft::Console::VirtualTerminal
std::wstring& params,
std::wstring& uri) const;

bool _CanSeqAcceptSubParam(const VTID id, const VTParameters& parameters) noexcept;

void _ClearLastChar() noexcept;
};
}
Loading