- Enter on a signal entry (net / explore) opens a modal popup to pick power / gnd / other. Recording is deduped: a sequence of toggles on the same signal collapses to a single `set-signal-type` line; no-op selections record nothing. - Bare interactive commands (the ones that open a full-screen mode) are no longer recorded by `script-save`. Their inline forms still are. Mutating actions inside a screen record their own canonical line. - Mentor importer treats signals whose name starts with `unconnected` as no-connect — the pin is kept on the part without a signal and tagged `ImportedUnconnected`. - `drop_singleton_signals` runs at the end of `load`: any signal with exactly one pin is detached (singletons are NC by definition); the pin is tagged `DroppedSingleton`. Count is reported inline. - `verify` gains a one-line orphan summary (imported NC / dropped singleton totals). Pins materialised by `FillIdentityNCs` are excluded via a `pin_map` filter — they are bridged to a real signal on the peer module and are not real NCs at system level. - NcOrigin tag is serialized in save snapshots as an optional 4th field on N records (backward-compatible). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
72 lines
2.2 KiB
C++
72 lines
2.2 KiB
C++
#include "tui/tui_helpers.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <cctype>
|
|
|
|
|
|
std::string ToLower(std::string s) {
|
|
std::transform(s.begin(), s.end(), s.begin(),
|
|
[](unsigned char c) { return std::tolower(c); });
|
|
return s;
|
|
}
|
|
|
|
bool NaturalLess(const std::string &a, const std::string &b) {
|
|
size_t i = 0, j = 0;
|
|
while (i < a.size() && j < b.size()) {
|
|
unsigned char ca = (unsigned char)a[i];
|
|
unsigned char cb = (unsigned char)b[j];
|
|
if (std::isdigit(ca) && std::isdigit(cb)) {
|
|
size_t za = 0, zb = 0;
|
|
while (i + za < a.size() && a[i + za] == '0') ++za;
|
|
while (j + zb < b.size() && b[j + zb] == '0') ++zb;
|
|
size_t ea = i + za;
|
|
while (ea < a.size() && std::isdigit((unsigned char)a[ea])) ++ea;
|
|
size_t eb = j + zb;
|
|
while (eb < b.size() && std::isdigit((unsigned char)b[eb])) ++eb;
|
|
size_t la = ea - (i + za);
|
|
size_t lb = eb - (j + zb);
|
|
if (la != lb) return la < lb;
|
|
int cmp = a.compare(i + za, la, b, j + zb, lb);
|
|
if (cmp != 0) return cmp < 0;
|
|
if (za != zb) return za > zb;
|
|
i = ea;
|
|
j = eb;
|
|
} else {
|
|
char la = (char)std::tolower(ca);
|
|
char lb = (char)std::tolower(cb);
|
|
if (la != lb) return la < lb;
|
|
++i; ++j;
|
|
}
|
|
}
|
|
if (i < a.size()) return false;
|
|
if (j < b.size()) return true;
|
|
return false;
|
|
}
|
|
|
|
std::string LongestCommonPrefix(const std::vector<std::string> &v) {
|
|
if (v.empty()) return "";
|
|
std::string lcp = v[0];
|
|
for (size_t i = 1; i < v.size(); ++i) {
|
|
size_t k = 0;
|
|
while (k < lcp.size() && k < v[i].size() && lcp[k] == v[i][k]) ++k;
|
|
lcp.resize(k);
|
|
}
|
|
return lcp;
|
|
}
|
|
|
|
std::vector<std::string> Tokenize(const std::string &s) {
|
|
std::vector<std::string> out;
|
|
std::string cur;
|
|
bool in_q = false;
|
|
for (char c : s) {
|
|
if (c == '"') { in_q = !in_q; continue; }
|
|
if (!in_q && std::isspace((unsigned char)c)) {
|
|
if (!cur.empty()) { out.push_back(std::move(cur)); cur.clear(); }
|
|
} else {
|
|
cur.push_back(c);
|
|
}
|
|
}
|
|
if (!cur.empty()) out.push_back(std::move(cur));
|
|
return out;
|
|
}
|