Skip to content

Commit 22f9835

Browse files
committed
Split xinspect.hpp into interface and implementation
1 parent ca8c42e commit 22f9835

File tree

3 files changed

+270
-236
lines changed

3 files changed

+270
-236
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ set(XEUS_CPP_HEADERS
209209
set(XEUS_CPP_SRC
210210
src/xholder.cpp
211211
src/xinput.cpp
212+
src/xinspect.cpp
212213
src/xinterpreter.cpp
213214
src/xoptions.cpp
214215
src/xparser.cpp

src/xinspect.cpp

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
/************************************************************************************
2+
* Copyright (c) 2023, xeus-cpp contributors *
3+
* *
4+
* Distributed under the terms of the BSD 3-Clause License. *
5+
* *
6+
* The full license is in the file LICENSE, distributed with this software. *
7+
************************************************************************************/
8+
9+
#include "xinspect.hpp"
10+
11+
#include "clang/Interpreter/CppInterOp.h"
12+
13+
namespace xcpp
14+
{
15+
bool node_predicate::operator()(pugi::xml_node node) const
16+
{
17+
return static_cast<std::string>(node.attribute("kind").value()) == kind
18+
&& static_cast<std::string>(node.child("name").child_value()) == child_value;
19+
}
20+
21+
std::string class_member_predicate::get_filename(pugi::xml_node node) const
22+
{
23+
for (pugi::xml_node child : node.children())
24+
{
25+
if (static_cast<std::string>(child.attribute("kind").value()) == kind
26+
&& static_cast<std::string>(child.child("name").child_value()) == child_value)
27+
{
28+
return child.child("anchorfile").child_value();
29+
}
30+
}
31+
return "";
32+
}
33+
34+
bool class_member_predicate::operator()(pugi::xml_node node) const
35+
{
36+
auto parent = (static_cast<std::string>(node.attribute("kind").value()) == "class"
37+
|| static_cast<std::string>(node.attribute("kind").value()) == "struct")
38+
&& static_cast<std::string>(node.child("name").child_value()) == class_name;
39+
if (parent)
40+
{
41+
for (pugi::xml_node child : node.children())
42+
{
43+
if (static_cast<std::string>(child.attribute("kind").value()) == kind
44+
&& static_cast<std::string>(child.child("name").child_value()) == child_value)
45+
{
46+
return true;
47+
}
48+
}
49+
}
50+
return false;
51+
}
52+
53+
std::string find_type_slow(const std::string& expression)
54+
{
55+
static unsigned long long var_count = 0;
56+
57+
if (auto* type = Cpp::GetType(expression))
58+
{
59+
return Cpp::GetQualifiedName(type);
60+
}
61+
62+
std::string id = "__Xeus_GetType_" + std::to_string(var_count++);
63+
std::string using_clause = "using " + id + " = __typeof__(" + expression + ");\n";
64+
65+
if (!Cpp::Declare(using_clause.c_str(), false))
66+
{
67+
Cpp::TCppScope_t lookup = Cpp::GetNamed(id, nullptr);
68+
Cpp::TCppType_t lookup_ty = Cpp::GetTypeFromScope(lookup);
69+
return Cpp::GetQualifiedCompleteName(Cpp::GetCanonicalType(lookup_ty));
70+
}
71+
return "";
72+
}
73+
74+
static nl::json read_tagconfs(const char* path)
75+
{
76+
nl::json result = nl::json::array();
77+
for (const auto& entry : std::filesystem::directory_iterator(path))
78+
{
79+
if (entry.path().extension() != ".json")
80+
{
81+
continue;
82+
}
83+
std::ifstream i(entry.path());
84+
nl::json json_entry;
85+
i >> json_entry;
86+
result.emplace_back(std::move(json_entry));
87+
}
88+
return result;
89+
}
90+
91+
std::pair<bool, std::smatch> is_inspect_request(const std::string& code, const std::regex& re)
92+
{
93+
std::smatch inspect;
94+
if (std::regex_search(code, inspect, re))
95+
{
96+
return std::make_pair(true, inspect);
97+
}
98+
return std::make_pair(false, inspect);
99+
}
100+
101+
void inspect(const std::string& code, nl::json& kernel_res)
102+
{
103+
std::string tagconf_dir = retrieve_tagconf_dir();
104+
std::string tagfiles_dir = retrieve_tagfile_dir();
105+
106+
nl::json tagconfs = read_tagconfs(tagconf_dir.c_str());
107+
108+
std::vector<std::string> check{"class", "struct", "function"};
109+
110+
std::string url, tagfile;
111+
112+
std::regex re_expression(R"((((?:\w*(?:\:{2}|\<.*\>|\(.*\)|\[.*\])?)\.?)*))");
113+
114+
std::smatch inspect = is_inspect_request(code, re_expression).second;
115+
116+
std::string inspect_result;
117+
118+
std::smatch method;
119+
std::string to_inspect = inspect[1];
120+
121+
// Method or variable of class found (xxxx.yyyy)
122+
if (std::regex_search(to_inspect, method, std::regex(R"((.*)\.(\w*)$)")))
123+
{
124+
std::string type_name = find_type_slow(method[1]);
125+
126+
if (!type_name.empty())
127+
{
128+
for (nl::json::const_iterator it = tagconfs.cbegin(); it != tagconfs.cend(); ++it)
129+
{
130+
url = it->at("url");
131+
tagfile = it->at("tagfile");
132+
std::string filename = tagfiles_dir + "/" + tagfile;
133+
pugi::xml_document doc;
134+
pugi::xml_parse_result result = doc.load_file(filename.c_str());
135+
class_member_predicate predicate{type_name, "function", method[2]};
136+
auto node = doc.find_node(predicate);
137+
if (!node.empty())
138+
{
139+
inspect_result = url + predicate.get_filename(node);
140+
}
141+
}
142+
}
143+
}
144+
else
145+
{
146+
std::string find_string;
147+
148+
// check if we try to find the documentation of a namespace
149+
// if yes, don't try to find the type using typeid
150+
std::regex is_namespace(R"(\w+(\:{2}\w+)+)");
151+
std::smatch namespace_match;
152+
if (std::regex_match(to_inspect, namespace_match, is_namespace))
153+
{
154+
find_string = to_inspect;
155+
}
156+
else
157+
{
158+
std::string type_name = find_type_slow(to_inspect);
159+
find_string = (type_name.empty()) ? to_inspect : type_name;
160+
}
161+
162+
for (nl::json::const_iterator it = tagconfs.cbegin(); it != tagconfs.cend(); ++it)
163+
{
164+
url = it->at("url");
165+
tagfile = it->at("tagfile");
166+
std::string filename = tagfiles_dir + "/" + tagfile;
167+
pugi::xml_document doc;
168+
pugi::xml_parse_result result = doc.load_file(filename.c_str());
169+
for (auto c : check)
170+
{
171+
node_predicate predicate{c, find_string};
172+
std::string node;
173+
174+
if (c == "class" || c == "struct")
175+
{
176+
node = doc.find_node(predicate).child("filename").child_value();
177+
}
178+
else
179+
{
180+
node = doc.find_node(predicate).child("anchorfile").child_value();
181+
}
182+
183+
if (!node.empty())
184+
{
185+
inspect_result = url + node;
186+
}
187+
}
188+
}
189+
}
190+
191+
if (inspect_result.empty())
192+
{
193+
std::cerr << "No documentation found for " << code << "\n";
194+
std::cout << std::flush;
195+
kernel_res["found"] = false;
196+
kernel_res["status"] = "error";
197+
kernel_res["ename"] = "No documentation found";
198+
kernel_res["evalue"] = "";
199+
kernel_res["traceback"] = nl::json::array();
200+
}
201+
else
202+
{
203+
// Format html content.
204+
std::string html_content = R"(<style>
205+
#pager-container {
206+
padding: 0;
207+
margin: 0;
208+
width: 100%;
209+
height: 100%;
210+
}
211+
.xcpp-iframe-pager {
212+
padding: 0;
213+
margin: 0;
214+
width: 100%;
215+
height: 100%;
216+
border: none;
217+
}
218+
</style>
219+
<iframe class="xcpp-iframe-pager" src=")"
220+
+ inspect_result + R"(?action=purge"></iframe>)";
221+
222+
// Note: Adding "?action=purge" suffix to force cppreference's
223+
// Mediawiki to purge the HTTP cache.
224+
225+
kernel_res["payload"] = nl::json::array();
226+
kernel_res["payload"][0] = nl::json::object(
227+
{{"data", {{"text/plain", inspect_result}, {"text/html", html_content}}},
228+
{"source", "page"},
229+
{"start", 0}}
230+
);
231+
kernel_res["user_expressions"] = nl::json::object();
232+
233+
std::cout << std::flush;
234+
kernel_res["found"] = true;
235+
kernel_res["status"] = "ok";
236+
}
237+
}
238+
239+
xintrospection::xintrospection()
240+
{
241+
pattern = spattern;
242+
}
243+
244+
void xintrospection::apply(const std::string& code, nl::json& kernel_res)
245+
{
246+
std::regex re(spattern + R"((.*))");
247+
std::smatch to_inspect;
248+
std::regex_search(code, to_inspect, re);
249+
inspect(to_inspect[1], kernel_res);
250+
}
251+
252+
std::unique_ptr<xpreamble> xintrospection::clone() const
253+
{
254+
return std::make_unique<xintrospection>(*this);
255+
}
256+
}

0 commit comments

Comments
 (0)