master
cpp 174 lines 4.15 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 TableOutput.cpp
8
9 Abstract:
10
11 Non-templated implementation for TableOutput: FormattedCell rendering and
12 the WrapText helper. The TableOutput<FieldCount> class template remains in
13 the header.
14
15 --*/
16 #include "precomp.h"
17 #include "TableOutput.h"
18
19 using namespace wsl::windows::common::vt;
20
21 namespace wsl::windows::wslc {
22
23 FormattedCell::FormattedCell(std::wstring_view text, const Sequence& seq) : sequences({&seq, &Format::Default})
24 {
25 WI_ASSERT(text.find(L"{}") == std::wstring_view::npos);
26 fmt.reserve(4 + text.size());
27 fmt += L"{}";
28 fmt += text;
29 fmt += L"{}";
30 }
31
32 size_t FormattedCell::VisibleWidth() const
33 {
34 if (sequences.empty())
35 {
36 return fmt.size();
37 }
38
39 size_t width = 0;
40 for (size_t i = 0; i < fmt.size(); ++i)
41 {
42 if (i + 1 < fmt.size() && fmt[i] == L'{' && fmt[i + 1] == L'}')
43 {
44 ++i; // skip the pair
45 }
46 else
47 {
48 ++width;
49 }
50 }
51 return width;
52 }
53
54 std::wstring FormattedCell::Render(bool vtEnabled, bool colorEnabled) const
55 {
56 if (sequences.empty())
57 {
58 return fmt; // plain text, no placeholders
59 }
60
61 std::wstring result;
62 result.reserve(fmt.size() + (vtEnabled ? sequences.size() * 8 : 0));
63 size_t seqIdx = 0;
64
65 for (size_t i = 0; i < fmt.size(); ++i)
66 {
67 if (i + 1 < fmt.size() && fmt[i] == L'{' && fmt[i + 1] == L'}')
68 {
69 if (vtEnabled && seqIdx < sequences.size() && (colorEnabled || !sequences[seqIdx]->IsColor()))
70 {
71 result.append(sequences[seqIdx]->Get());
72 }
73 ++seqIdx;
74 ++i; // skip the pair
75 }
76 else
77 {
78 result += fmt[i];
79 }
80 }
81
82 return result;
83 }
84
85 std::wstring FormattedCell::RenderTruncated(size_t maxWidth, bool vtEnabled, bool colorEnabled) const
86 {
87 if (sequences.empty())
88 {
89 // Plain text: simple truncation.
90 if (fmt.size() <= maxWidth)
91 {
92 return fmt;
93 }
94 return fmt.substr(0, maxWidth > 0 ? maxWidth - 1 : 0) + L"\u2026";
95 }
96
97 std::wstring result;
98 result.reserve(fmt.size());
99 size_t seqIdx = 0;
100 size_t visibleChars = 0;
101 bool truncated = (maxWidth == 0);
102 const size_t truncateAt = maxWidth > 1 ? maxWidth - 1 : 0;
103
104 for (size_t i = 0; i < fmt.size(); ++i)
105 {
106 if (i + 1 < fmt.size() && fmt[i] == L'{' && fmt[i + 1] == L'}')
107 {
108 // Always emit sequences (they're invisible); they handle resets after truncation.
109 if (vtEnabled && seqIdx < sequences.size() && (colorEnabled || !sequences[seqIdx]->IsColor()))
110 {
111 result.append(sequences[seqIdx]->Get());
112 }
113 ++seqIdx;
114 ++i;
115 }
116 else if (!truncated)
117 {
118 if (visibleChars < truncateAt)
119 {
120 result += fmt[i];
121 ++visibleChars;
122 }
123 else
124 {
125 result += L'\u2026';
126 truncated = true;
127 }
128 }
129 // After truncation, skip remaining visible chars but continue to emit sequences.
130 }
131
132 return result;
133 }
134
135 namespace details {
136
137 std::vector<std::wstring> WrapText(const std::wstring& text, size_t maxWidth)
138 {
139 if (maxWidth == 0 || text.length() <= maxWidth)
140 {
141 return {text};
142 }
143
144 std::vector<std::wstring> lines;
145 size_t pos = 0;
146
147 while (pos < text.length())
148 {
149 size_t chunkEnd = std::min(pos + maxWidth, text.length());
150
151 if (chunkEnd < text.length())
152 {
153 size_t breakAt = text.rfind(L' ', chunkEnd);
154 if (breakAt != std::wstring::npos && breakAt > pos)
155 {
156 chunkEnd = breakAt;
157 }
158 }
159
160 lines.emplace_back(text.substr(pos, chunkEnd - pos));
161
162 pos = chunkEnd;
163 while (pos < text.length() && text[pos] == L' ')
164 {
165 ++pos;
166 }
167 }
168
169 return lines;
170 }
171
172 } // namespace details
173
174 } // namespace wsl::windows::wslc