WIP: G-code find & replace: Support for non-regular expression,
whole word and case insensitive search.
This commit is contained in:
parent
add1e994fa
commit
d4fd95bd4a
@ -2849,14 +2849,11 @@ void GCode::GCodeOutputStream::close()
|
|||||||
void GCode::GCodeOutputStream::write(const char *what)
|
void GCode::GCodeOutputStream::write(const char *what)
|
||||||
{
|
{
|
||||||
if (what != nullptr) {
|
if (what != nullptr) {
|
||||||
const char* gcode = what;
|
|
||||||
// writes string to file
|
|
||||||
fwrite(gcode, 1, ::strlen(gcode), this->f);
|
|
||||||
//FIXME don't allocate a string, maybe process a batch of lines?
|
//FIXME don't allocate a string, maybe process a batch of lines?
|
||||||
if (m_find_replace)
|
std::string gcode(m_find_replace ? m_find_replace->process_layer(what) : what);
|
||||||
m_processor.process_buffer(m_find_replace->process_layer(std::string(gcode)));
|
// writes string to file
|
||||||
else
|
fwrite(gcode.c_str(), 1, gcode.size(), this->f);
|
||||||
m_processor.process_buffer(std::string(gcode));
|
m_processor.process_buffer(gcode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include "FindReplace.hpp"
|
#include "FindReplace.hpp"
|
||||||
#include "../Utils.hpp"
|
#include "../Utils.hpp"
|
||||||
|
|
||||||
|
#include <cctype> // isalpha
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
GCodeFindReplace::GCodeFindReplace(const PrintConfig &print_config)
|
GCodeFindReplace::GCodeFindReplace(const PrintConfig &print_config)
|
||||||
@ -12,13 +14,24 @@ GCodeFindReplace::GCodeFindReplace(const PrintConfig &print_config)
|
|||||||
|
|
||||||
m_substitutions.reserve(subst.size() / 3);
|
m_substitutions.reserve(subst.size() / 3);
|
||||||
for (size_t i = 0; i < subst.size(); i += 3) {
|
for (size_t i = 0; i < subst.size(); i += 3) {
|
||||||
boost::regex pattern;
|
Substitution out;
|
||||||
try {
|
try {
|
||||||
pattern.assign(subst[i], boost::regex::optimize); // boost::regex::icase
|
out.plain_pattern = subst[i];
|
||||||
|
out.format = subst[i + 1];
|
||||||
|
const std::string ¶ms = subst[i + 2];
|
||||||
|
out.regexp = strchr(params.c_str(), 'r') != nullptr || strchr(params.c_str(), 'R') != nullptr;
|
||||||
|
out.case_insensitive = strchr(params.c_str(), 'i') != nullptr || strchr(params.c_str(), 'I') != nullptr;
|
||||||
|
out.whole_word = strchr(params.c_str(), 'w') != nullptr || strchr(params.c_str(), 'W') != nullptr;
|
||||||
|
if (out.regexp)
|
||||||
|
out.regexp_pattern.assign(
|
||||||
|
out.whole_word ?
|
||||||
|
std::string("\b") + out.plain_pattern + "\b" :
|
||||||
|
out.plain_pattern,
|
||||||
|
(out.case_insensitive ? boost::regex::icase : 0) | boost::regex::optimize);
|
||||||
} catch (const std::exception &ex) {
|
} catch (const std::exception &ex) {
|
||||||
throw RuntimeError(std::string("Invalid gcode_substitutions parameter, failed to compile regular expression: ") + ex.what());
|
throw RuntimeError(std::string("Invalid gcode_substitutions parameter, failed to compile regular expression: ") + ex.what());
|
||||||
}
|
}
|
||||||
m_substitutions.push_back({ std::move(pattern), subst[i + 1] });
|
m_substitutions.emplace_back(std::move(out));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,6 +62,29 @@ private:
|
|||||||
std::string *m_data;
|
std::string *m_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename FindFn>
|
||||||
|
static void find_and_replace_whole_word(std::string &inout, const std::string &match, const std::string &replace, FindFn find_fn)
|
||||||
|
{
|
||||||
|
if (! match.empty() && inout.size() >= match.size() && match != replace) {
|
||||||
|
std::string out;
|
||||||
|
auto [i, j] = find_fn(inout, 0, match);
|
||||||
|
size_t k = 0;
|
||||||
|
for (; i != std::string::npos; std::tie(i, j) = find_fn(inout, i, match)) {
|
||||||
|
if ((i == 0 || ! std::isalnum(inout[i - 1])) && (j == inout.size() || ! std::isalnum(inout[j]))) {
|
||||||
|
out.reserve(inout.size());
|
||||||
|
out.append(inout, k, i - k);
|
||||||
|
out.append(replace);
|
||||||
|
i = k = j;
|
||||||
|
} else
|
||||||
|
i += match.size();
|
||||||
|
}
|
||||||
|
if (k > 0) {
|
||||||
|
out.append(inout, k, inout.size() - k);
|
||||||
|
inout.swap(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string GCodeFindReplace::process_layer(const std::string &ain)
|
std::string GCodeFindReplace::process_layer(const std::string &ain)
|
||||||
{
|
{
|
||||||
std::string out;
|
std::string out;
|
||||||
@ -57,11 +93,39 @@ std::string GCodeFindReplace::process_layer(const std::string &ain)
|
|||||||
temp.reserve(in->size());
|
temp.reserve(in->size());
|
||||||
|
|
||||||
for (const Substitution &substitution : m_substitutions) {
|
for (const Substitution &substitution : m_substitutions) {
|
||||||
temp.clear();
|
if (substitution.regexp) {
|
||||||
temp.reserve(in->size());
|
temp.clear();
|
||||||
boost::regex_replace(ToStringIterator(temp), in->begin(), in->end(),
|
temp.reserve(in->size());
|
||||||
substitution.pattern, substitution.format, boost::match_default | boost::match_not_dot_newline | boost::match_not_dot_null | boost::format_all);
|
boost::regex_replace(ToStringIterator(temp), in->begin(), in->end(),
|
||||||
std::swap(out, temp);
|
substitution.regexp_pattern, substitution.format, boost::match_default | boost::match_not_dot_newline | boost::match_not_dot_null | boost::format_all);
|
||||||
|
std::swap(out, temp);
|
||||||
|
} else {
|
||||||
|
if (in == &ain)
|
||||||
|
out = ain;
|
||||||
|
// Plain substitution
|
||||||
|
if (substitution.case_insensitive) {
|
||||||
|
if (substitution.whole_word)
|
||||||
|
find_and_replace_whole_word(out, substitution.plain_pattern, substitution.format,
|
||||||
|
[](const std::string &str, size_t start_pos, const std::string &match) {
|
||||||
|
auto begin = str.begin() + start_pos;
|
||||||
|
auto res = boost::ifind_first(
|
||||||
|
boost::iterator_range<std::string::const_iterator>(begin, str.end()),
|
||||||
|
boost::iterator_range<std::string::const_iterator>(match.begin(), match.end()));
|
||||||
|
return res ? std::make_pair(size_t(res.begin() - begin), size_t(res.end() - begin)) : std::make_pair(std::string::npos, std::string::npos);
|
||||||
|
});
|
||||||
|
else
|
||||||
|
boost::ireplace_all(out, substitution.plain_pattern, substitution.format);
|
||||||
|
} else {
|
||||||
|
if (substitution.whole_word)
|
||||||
|
find_and_replace_whole_word(out, substitution.plain_pattern, substitution.format,
|
||||||
|
[](const std::string &str, size_t start_pos, const std::string &match) {
|
||||||
|
size_t pos = str.find(match, start_pos);
|
||||||
|
return std::make_pair(pos, pos + (pos == std::string::npos ? 0 : match.size()));
|
||||||
|
});
|
||||||
|
else
|
||||||
|
boost::replace_all(out, substitution.plain_pattern, substitution.format);
|
||||||
|
}
|
||||||
|
}
|
||||||
in = &out;
|
in = &out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,8 +15,13 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
struct Substitution {
|
struct Substitution {
|
||||||
boost::regex pattern;
|
std::string plain_pattern;
|
||||||
std::string format;
|
boost::regex regexp_pattern;
|
||||||
|
std::string format;
|
||||||
|
|
||||||
|
bool regexp { false };
|
||||||
|
bool case_insensitive { false };
|
||||||
|
bool whole_word { false };
|
||||||
};
|
};
|
||||||
std::vector<Substitution> m_substitutions;
|
std::vector<Substitution> m_substitutions;
|
||||||
};
|
};
|
||||||
|
@ -641,6 +641,11 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||||||
((ConfigOptionBool, gcode_comments))
|
((ConfigOptionBool, gcode_comments))
|
||||||
((ConfigOptionEnum<GCodeFlavor>, gcode_flavor))
|
((ConfigOptionEnum<GCodeFlavor>, gcode_flavor))
|
||||||
((ConfigOptionBool, gcode_label_objects))
|
((ConfigOptionBool, gcode_label_objects))
|
||||||
|
// Triples of strings: "search pattern", "replace with pattern", "attribs"
|
||||||
|
// where "attribs" are one of:
|
||||||
|
// r - regular expression
|
||||||
|
// i - case insensitive
|
||||||
|
// w - whole word
|
||||||
((ConfigOptionStrings, gcode_substitutions))
|
((ConfigOptionStrings, gcode_substitutions))
|
||||||
((ConfigOptionString, layer_gcode))
|
((ConfigOptionString, layer_gcode))
|
||||||
((ConfigOptionFloat, max_print_speed))
|
((ConfigOptionFloat, max_print_speed))
|
||||||
|
Loading…
Reference in New Issue
Block a user