Skip to content

Commit 21988cf

Browse files
committed
feat: Formatter
1 parent c916310 commit 21988cf

File tree

1 file changed

+208
-0
lines changed

1 file changed

+208
-0
lines changed

include/mrdox/Support/Formatter.hpp

+208
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
//
2+
// Licensed under the Apache License v2.0 with LLVM Exceptions.
3+
// See https://llvm.org/LICENSE.txt for license information.
4+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5+
//
6+
// Copyright (c) 2023 Vinnie Falco ([email protected])
7+
//
8+
// Official repository: https://github.com/cppalliance/mrdox
9+
//
10+
11+
#ifndef MRDOX_SUPPORT_FORMATTER_HPP
12+
#define MRDOX_SUPPORT_FORMATTER_HPP
13+
14+
#include <mrdox/Platform.hpp>
15+
#include <llvm/ADT/SmallVector.h>
16+
#include <charconv>
17+
#include <concepts>
18+
#include <string_view>
19+
#include <type_traits>
20+
21+
/* This API offers formatted output tailored to
22+
the needs of generators that emit text.
23+
*/
24+
25+
namespace clang {
26+
namespace mrdox {
27+
28+
struct format_tag {};
29+
30+
/** Produces formatted output to a stream.
31+
*/
32+
class Formatter
33+
{
34+
void* stream_;
35+
void (*stream_fn_)(
36+
void*, char const*, std::size_t);
37+
std::string indent_;
38+
bool needIndent_ = true;
39+
40+
public:
41+
/** Constructor.
42+
*/
43+
template<class Stream>
44+
explicit
45+
Formatter(
46+
Stream& os) noexcept
47+
: stream_(&os)
48+
, stream_fn_(
49+
[](void* os, char const* p, std::size_t n)
50+
{
51+
reinterpret_cast<Stream*>(os)->write(p, n);
52+
})
53+
{
54+
}
55+
56+
/** Set the indentation level.
57+
58+
@return The previous indentation level.
59+
*/
60+
std::size_t indent(int n)
61+
{
62+
auto const n0 = indent_.size();
63+
indent_.resize(n0 + n, ' ');
64+
return n0;
65+
}
66+
67+
/** Write a line of formatted output.
68+
69+
This function efficiently converts each
70+
argument to a string one at a time and
71+
writes them to the output. After all the
72+
arguments are written, a newline is emitted.
73+
74+
The string will be printed at the current
75+
indentation level.
76+
*/
77+
template<class Arg0, class... ArgN>
78+
void
79+
operator()(Arg0&& arg0, ArgN&&... argn)
80+
{
81+
if constexpr(sizeof...(argn) == 0)
82+
{
83+
write(std::forward<Arg0>(arg0));
84+
}
85+
else
86+
{
87+
write(std::forward<Arg0>(arg0));
88+
(*this)(std::forward<ArgN>(argn)...);
89+
}
90+
}
91+
92+
void operator()() const noexcept = delete;
93+
94+
private:
95+
// emit a newline
96+
void write_newline()
97+
{
98+
stream_fn_(stream_, "\n", 1);
99+
}
100+
101+
// write the string to the stream
102+
void write_impl(std::string_view s)
103+
{
104+
auto it = s.data();
105+
auto const end = it + s.size();
106+
auto const flush =
107+
[this](auto it0, auto it)
108+
{
109+
if(needIndent_)
110+
{
111+
stream_fn_(stream_, indent_.data(), indent_.size());
112+
needIndent_ = false;
113+
}
114+
stream_fn_(stream_, it0, it - it0);
115+
};
116+
for(;;)
117+
{
118+
if(it == end)
119+
break;
120+
if(*it != '\n')
121+
{
122+
do_chars:
123+
auto it0 = it;
124+
for(;;)
125+
{
126+
++it;
127+
if(it == end)
128+
return flush(it0, it);
129+
if(*it != '\n')
130+
continue;
131+
flush(it0, it);
132+
break;
133+
}
134+
}
135+
needIndent_ = true;
136+
for(;;)
137+
{
138+
write_newline();
139+
++it;
140+
if(it == end)
141+
return;
142+
if(*it == '\n')
143+
continue;
144+
goto do_chars;
145+
}
146+
}
147+
}
148+
149+
// string literal
150+
void write(char const* sz)
151+
{
152+
write_impl(std::string_view(sz));
153+
}
154+
155+
// small string
156+
void write(llvm::SmallVectorImpl<char> const& ss)
157+
{
158+
write_impl(std::string_view(ss.data(), ss.size()));
159+
}
160+
161+
void write(std::string const& s)
162+
{
163+
write_impl(std::string_view(s.data(), s.size()));
164+
}
165+
166+
// convertible to std::string_view
167+
template<class T>
168+
requires
169+
std::is_convertible_v<T, std::string_view>
170+
void write(T const& t)
171+
{
172+
write_impl(std::string_view(t));
173+
}
174+
175+
// passed to std::to_chars
176+
template<typename N>
177+
requires
178+
requires(N n) { std::to_chars(n); }
179+
void write(N n)
180+
{
181+
write_impl(std::to_chars(n));
182+
}
183+
184+
// call operator
185+
template<class T>
186+
void write(T const& t)
187+
requires requires { t(*this); }
188+
{
189+
t(*this);
190+
}
191+
192+
193+
// tag_invoke
194+
template<class T>
195+
void write(T const& t)
196+
requires requires
197+
{
198+
tag_invoke(format_tag{}, *this, t);
199+
}
200+
{
201+
tag_invoke(format_tag{}, *this, t);
202+
}
203+
};
204+
205+
} // mrdox
206+
} // clang
207+
208+
#endif

0 commit comments

Comments
 (0)