2bf5d18c84
This pull request is the initial implementation of hyperlink auto detection Overall design: - Upon startup, TerminalCore gives the TextBuffer some patterns it should know about - Whenever something in the viewport changes (i.e. text output/scrolling), TerminalControl tells TerminalCore (through a throttled function for performance) to retrieve the visible pattern locations from the TextBuffer - When the renderer encounters a region that is associated with a pattern, it paints that region differently References #5001 Closes #574
244 lines
9.7 KiB
C++
244 lines
9.7 KiB
C++
/*++
|
|
Copyright (c) Microsoft Corporation
|
|
Licensed under the MIT license.
|
|
|
|
Module Name:
|
|
- textBuffer.hpp
|
|
|
|
Abstract:
|
|
- This module contains structures and functions for manipulating a text
|
|
based buffer within the console host window.
|
|
|
|
Author(s):
|
|
- Michael Niksa (miniksa) 10-Apr-2014
|
|
- Paul Campbell (paulcam) 10-Apr-2014
|
|
|
|
Revision History:
|
|
- From components of output.h/.c
|
|
by Therese Stowell (ThereseS) 1990-1991
|
|
|
|
Notes:
|
|
ScreenBuffer data structure overview:
|
|
|
|
each screen buffer has an array of ROW structures. each ROW structure
|
|
contains the data for one row of text. the data stored for one row of
|
|
text is a character array and an attribute array. the character array
|
|
is allocated the full length of the row from the heap, regardless of the
|
|
non-space length. we also maintain the non-space length. the character
|
|
array is initialized to spaces. the attribute
|
|
array is run length encoded (i.e 5 BLUE, 3 RED). if there is only one
|
|
attribute for the whole row (the normal case), it is stored in the ATTR_ROW
|
|
structure. otherwise the attr string is allocated from the heap.
|
|
|
|
ROW - CHAR_ROW - CHAR string
|
|
\ \ length of char string
|
|
\
|
|
ATTR_ROW - ATTR_PAIR string
|
|
\ length of attr pair string
|
|
ROW
|
|
ROW
|
|
ROW
|
|
|
|
ScreenInfo->Rows points to the ROW array. ScreenInfo->Rows[0] is not
|
|
necessarily the top row. ScreenInfo->BufferInfo.TextInfo->FirstRow contains the index of
|
|
the top row. That means scrolling (if scrolling entire screen)
|
|
merely involves changing the FirstRow index,
|
|
filling in the last row, and updating the screen.
|
|
|
|
--*/
|
|
|
|
#pragma once
|
|
|
|
#include "cursor.h"
|
|
#include "Row.hpp"
|
|
#include "TextAttribute.hpp"
|
|
#include "UnicodeStorage.hpp"
|
|
#include "../types/inc/Viewport.hpp"
|
|
|
|
#include "../buffer/out/textBufferCellIterator.hpp"
|
|
#include "../buffer/out/textBufferTextIterator.hpp"
|
|
|
|
#include "../renderer/inc/IRenderTarget.hpp"
|
|
|
|
class TextBuffer final
|
|
{
|
|
public:
|
|
TextBuffer(const COORD screenBufferSize,
|
|
const TextAttribute defaultAttributes,
|
|
const UINT cursorSize,
|
|
Microsoft::Console::Render::IRenderTarget& renderTarget);
|
|
TextBuffer(const TextBuffer& a) = delete;
|
|
|
|
// Used for duplicating properties to another text buffer
|
|
void CopyProperties(const TextBuffer& OtherBuffer) noexcept;
|
|
|
|
// row manipulation
|
|
const ROW& GetRowByOffset(const size_t index) const;
|
|
ROW& GetRowByOffset(const size_t index);
|
|
|
|
TextBufferCellIterator GetCellDataAt(const COORD at) const;
|
|
TextBufferCellIterator GetCellLineDataAt(const COORD at) const;
|
|
TextBufferCellIterator GetCellDataAt(const COORD at, const Microsoft::Console::Types::Viewport limit) const;
|
|
TextBufferTextIterator GetTextDataAt(const COORD at) const;
|
|
TextBufferTextIterator GetTextLineDataAt(const COORD at) const;
|
|
TextBufferTextIterator GetTextDataAt(const COORD at, const Microsoft::Console::Types::Viewport limit) const;
|
|
|
|
// Text insertion functions
|
|
OutputCellIterator Write(const OutputCellIterator givenIt);
|
|
|
|
OutputCellIterator Write(const OutputCellIterator givenIt,
|
|
const COORD target,
|
|
const std::optional<bool> wrap = true);
|
|
|
|
OutputCellIterator WriteLine(const OutputCellIterator givenIt,
|
|
const COORD target,
|
|
const std::optional<bool> setWrap = std::nullopt,
|
|
const std::optional<size_t> limitRight = std::nullopt);
|
|
|
|
bool InsertCharacter(const wchar_t wch, const DbcsAttribute dbcsAttribute, const TextAttribute attr);
|
|
bool InsertCharacter(const std::wstring_view chars, const DbcsAttribute dbcsAttribute, const TextAttribute attr);
|
|
bool IncrementCursor();
|
|
bool NewlineCursor();
|
|
|
|
// Scroll needs access to this to quickly rotate around the buffer.
|
|
bool IncrementCircularBuffer(const bool inVtMode = false);
|
|
|
|
COORD GetLastNonSpaceCharacter(std::optional<const Microsoft::Console::Types::Viewport> viewOptional = std::nullopt) const;
|
|
|
|
Cursor& GetCursor() noexcept;
|
|
const Cursor& GetCursor() const noexcept;
|
|
|
|
const SHORT GetFirstRowIndex() const noexcept;
|
|
|
|
const Microsoft::Console::Types::Viewport GetSize() const noexcept;
|
|
|
|
void ScrollRows(const SHORT firstRow, const SHORT size, const SHORT delta);
|
|
|
|
UINT TotalRowCount() const noexcept;
|
|
|
|
[[nodiscard]] TextAttribute GetCurrentAttributes() const noexcept;
|
|
|
|
void SetCurrentAttributes(const TextAttribute& currentAttributes) noexcept;
|
|
|
|
void Reset();
|
|
|
|
[[nodiscard]] HRESULT ResizeTraditional(const COORD newSize) noexcept;
|
|
|
|
const UnicodeStorage& GetUnicodeStorage() const noexcept;
|
|
UnicodeStorage& GetUnicodeStorage() noexcept;
|
|
|
|
Microsoft::Console::Render::IRenderTarget& GetRenderTarget() noexcept;
|
|
|
|
const COORD GetWordStart(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode = false) const;
|
|
const COORD GetWordEnd(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode = false) const;
|
|
bool MoveToNextWord(COORD& pos, const std::wstring_view wordDelimiters, COORD lastCharPos) const;
|
|
bool MoveToPreviousWord(COORD& pos, const std::wstring_view wordDelimiters) const;
|
|
|
|
const til::point GetGlyphStart(const til::point pos) const;
|
|
const til::point GetGlyphEnd(const til::point pos) const;
|
|
bool MoveToNextGlyph(til::point& pos, bool allowBottomExclusive = false) const;
|
|
bool MoveToPreviousGlyph(til::point& pos) const;
|
|
|
|
const std::vector<SMALL_RECT> GetTextRects(COORD start, COORD end, bool blockSelection = false) const;
|
|
|
|
void AddHyperlinkToMap(std::wstring_view uri, uint16_t id);
|
|
std::wstring GetHyperlinkUriFromId(uint16_t id) const;
|
|
uint16_t GetHyperlinkId(std::wstring_view uri, std::wstring_view id);
|
|
void RemoveHyperlinkFromMap(uint16_t id);
|
|
std::wstring GetCustomIdFromId(uint16_t id) const;
|
|
void CopyHyperlinkMaps(const TextBuffer& OtherBuffer);
|
|
|
|
class TextAndColor
|
|
{
|
|
public:
|
|
std::vector<std::wstring> text;
|
|
std::vector<std::vector<COLORREF>> FgAttr;
|
|
std::vector<std::vector<COLORREF>> BkAttr;
|
|
};
|
|
|
|
const TextAndColor GetText(const bool lineSelection,
|
|
const bool trimTrailingWhitespace,
|
|
const std::vector<SMALL_RECT>& textRects,
|
|
std::function<std::pair<COLORREF, COLORREF>(const TextAttribute&)> GetAttributeColors = nullptr) const;
|
|
|
|
static std::string GenHTML(const TextAndColor& rows,
|
|
const int fontHeightPoints,
|
|
const std::wstring_view fontFaceName,
|
|
const COLORREF backgroundColor);
|
|
|
|
static std::string GenRTF(const TextAndColor& rows,
|
|
const int fontHeightPoints,
|
|
const std::wstring_view fontFaceName,
|
|
const COLORREF backgroundColor);
|
|
|
|
struct PositionInformation
|
|
{
|
|
short mutableViewportTop{ 0 };
|
|
short visibleViewportTop{ 0 };
|
|
};
|
|
|
|
static HRESULT Reflow(TextBuffer& oldBuffer,
|
|
TextBuffer& newBuffer,
|
|
const std::optional<Microsoft::Console::Types::Viewport> lastCharacterViewport,
|
|
std::optional<std::reference_wrapper<PositionInformation>> positionInfo);
|
|
|
|
const size_t AddPatternRecognizer(const std::wstring_view regexString);
|
|
void CopyPatterns(const TextBuffer& OtherBuffer);
|
|
interval_tree::IntervalTree<til::point, size_t> GetPatterns(const size_t firstRow, const size_t lastRow) const;
|
|
|
|
private:
|
|
void _UpdateSize();
|
|
Microsoft::Console::Types::Viewport _size;
|
|
std::deque<ROW> _storage;
|
|
Cursor _cursor;
|
|
|
|
SHORT _firstRow; // indexes top row (not necessarily 0)
|
|
|
|
TextAttribute _currentAttributes;
|
|
|
|
// storage location for glyphs that can't fit into the buffer normally
|
|
UnicodeStorage _unicodeStorage;
|
|
|
|
std::unordered_map<uint16_t, std::wstring> _hyperlinkMap;
|
|
std::unordered_map<std::wstring, uint16_t> _hyperlinkCustomIdMap;
|
|
uint16_t _currentHyperlinkId;
|
|
|
|
void _RefreshRowIDs(std::optional<SHORT> newRowWidth);
|
|
|
|
Microsoft::Console::Render::IRenderTarget& _renderTarget;
|
|
|
|
void _SetFirstRowIndex(const SHORT FirstRowIndex) noexcept;
|
|
|
|
COORD _GetPreviousFromCursor() const noexcept;
|
|
|
|
void _SetWrapOnCurrentRow();
|
|
void _AdjustWrapOnCurrentRow(const bool fSet);
|
|
|
|
void _NotifyPaint(const Microsoft::Console::Types::Viewport& viewport) const;
|
|
|
|
// Assist with maintaining proper buffer state for Double Byte character sequences
|
|
bool _PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute);
|
|
bool _AssertValidDoubleByteSequence(const DbcsAttribute dbcsAttribute);
|
|
|
|
ROW& _GetFirstRow();
|
|
ROW& _GetPrevRowNoWrap(const ROW& row);
|
|
|
|
void _ExpandTextRow(SMALL_RECT& selectionRow) const;
|
|
|
|
const DelimiterClass _GetDelimiterClassAt(const COORD pos, const std::wstring_view wordDelimiters) const;
|
|
const COORD _GetWordStartForAccessibility(const COORD target, const std::wstring_view wordDelimiters) const;
|
|
const COORD _GetWordStartForSelection(const COORD target, const std::wstring_view wordDelimiters) const;
|
|
const COORD _GetWordEndForAccessibility(const COORD target, const std::wstring_view wordDelimiters, const COORD lastCharPos) const;
|
|
const COORD _GetWordEndForSelection(const COORD target, const std::wstring_view wordDelimiters) const;
|
|
|
|
void _PruneHyperlinks();
|
|
|
|
std::unordered_map<size_t, std::wstring> _idsAndPatterns;
|
|
size_t _currentPatternId;
|
|
|
|
#ifdef UNIT_TESTING
|
|
friend class TextBufferTests;
|
|
friend class UiaTextRangeTests;
|
|
#endif
|
|
};
|