Skip to content

Migrate project (xml to json)#27

Merged
jgeudens merged 4 commits intomasterfrom
claude/xml-to-json-migration-7Y842
Apr 13, 2026
Merged

Migrate project (xml to json)#27
jgeudens merged 4 commits intomasterfrom
claude/xml-to-json-migration-7Y842

Conversation

@jgeudens
Copy link
Copy Markdown
Member

@jgeudens jgeudens commented Apr 10, 2026

Summary by CodeRabbit

  • New Features

    • Added support for importing legacy XML-format project files.
    • Automatic project file format detection (JSON vs XML) on import.
  • Tests

    • Added comprehensive test suite and fixtures for XML parser covering data-level boundaries, connections/devices, logging, scaling and value-axis scenarios.

claude and others added 3 commits April 10, 2026 22:10
… 3-5)

Reintroduce read-only support for the old XML-based .mbs project file
format. The XML parser converts legacy files into the same ProjectSettings
structure used by the JSON parser, including constructing the adapter
settings JSON blob from XML connection/device elements. Saving always
produces JSON v6, effectively auto-migrating old files.

All XML-specific code is isolated in separate files (ProjectFileXmlParser)
for easy future removal. The only change to existing code is a small
format-detection branch in ProjectFileHandler::openProjectFile().

https://claude.ai/code/session_01Lyu9U25MtH34zG7gUFLe2u
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 10, 2026

Walkthrough

This pull request introduces XML project file parsing for legacy ModbusScope project files (data levels 3–5). It adds a new ProjectFileXmlParser class that parses XML-formatted project files, a format-detection mechanism in ProjectFileHandler to select between JSON and XML parsers, new project-file constants, and a comprehensive test suite validating XML parsing across multiple data-level and configuration scenarios.

Changes

Cohort / File(s) Summary
Code Formatting
src/communication/modbuspoll.cpp, src/dialogs/addregisterwidget.cpp
Minor whitespace and formatting adjustments (removed blank lines; reformatted a Qt connect call).
Project File Definitions
src/importexport/projectfiledefinitions.h
Added new XML/JSON tag and key constants for connections and connection fields (e.g., cConnectionTag, cConnectionIdTag, cIpTag, cPortTag, cBaudrateTag, cParityTag, cSlaveIdTag, cConnectionsJsonKey, cConnectionTypeJsonKey).
Project File Handler
src/importexport/projectfilehandler.cpp
Added format-detection: trims file content and selects JSON parser if first non-whitespace is {, XML parser if <, otherwise reports invalid project file.
XML Parser Implementation
src/importexport/projectfilexmlparser.h, src/importexport/projectfilexmlparser.cpp
New ProjectFileXmlParser class with public parseFile(...) and ~14 helper methods to parse <modbus>, <scope>, <view>, connections, devices, registers, logs and scales; builds adapter JSON settings and populates ProjectSettings.
Tests & Test Data
tests/importexport/CMakeLists.txt, tests/importexport/projectfilexmltestdata.h, tests/importexport/projectfilexmltestdata.cpp, tests/importexport/tst_projectfilexmlparser.h, tests/importexport/tst_projectfilexmlparser.cpp
Added test target and comprehensive test suite with multiple XML fixtures covering data-level boundaries, legacy/mixed connection formats, register expressions, log file handling (including relative path resolution), and scale/value-axis behaviours.

Sequence Diagram

sequenceDiagram
    actor User
    participant App as Application
    participant Handler as ProjectFileHandler
    participant Detector as FormatDetector
    participant JsonParser as ProjectFileJsonParser
    participant XmlParser as ProjectFileXmlParser
    participant DOM as QDomDocument
    participant Settings as ProjectSettings

    User->>App: Open project file
    App->>Handler: openProjectFile(fileContent)
    Handler->>Detector: Trim & inspect first character

    alt First char is '{'
        Detector->>JsonParser: Select JSON parser
        JsonParser->>Settings: parseFile(content, pSettings)
    else First char is '<'
        Detector->>XmlParser: Select XML parser
        XmlParser->>DOM: Load XML content
        DOM-->>XmlParser: QDomDocument parsed
        XmlParser->>XmlParser: Validate root & datalevel
        XmlParser->>XmlParser: Parse modbus/scope/view tags
        XmlParser->>Settings: populateSettings(pSettings)
    else Invalid format
        Detector->>Handler: Report parse error
        Handler->>App: Display "not a valid MBS project file" error
    end

    XmlParser-->>Handler: GeneralError result
    Handler->>App: Display result or error
Loading

Possibly related PRs

  • Update device + remove connection #14 — Touches project-file import/export and projectfiledefinitions constants; closely related to the added XML parsing and connection constants.
  • UI names #13 — Adds adapter connection/device configuration schema using the same field names (ip, port, baudrate, parity, stopbits, slaveId, etc.), aligning with this PR's connection field extraction and adapter JSON.
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.74% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: adding XML project file parsing capability to support legacy file migration to JSON format.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/xml-to-json-migration-7Y842

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/importexport/projectfilexmlparser.cpp`:
- Around line 238-255: The parsing currently casts the unsigned int from
child.text().toUInt(&bRet) directly to quint8 for
ProjectFileDefinitions::cSlaveIdTag and ::cConsecutiveMaxTag (variables slaveId
and consecutiveMax), allowing out-of-range values (e.g. 300) to wrap; change the
logic to first parse to an unsigned integer, verify bRet is true and that the
parsed value <= 255 (or std::numeric_limits<quint8>::max()), report the same
parseErr on failure, and only then assign/cast to quint8; apply the identical
range check to the other occurrence noted (around lines 334-351) so both parsing
sites reject out-of-range 8-bit values before narrowing.
- Around line 429-444: The parser currently validates log file paths using
QFileInfo(child.text()) which treats relative paths as relative to the process
CWD and can drop valid project-relative paths; modify the logic in
projectfilexmlparser.cpp (the block building QFileInfo from child.text() and
setting pLogSettings->bLogToFileFile / pLogSettings->logFile) to stop using CWD
for relative-path checks: either accept the path without checking
QFileInfo.dir().exists() for relative paths or construct QFileInfo/QDir relative
to the project file base directory (pass in the project base dir into the parser
and use QFileInfo(projectBaseDir, child.text()) or
QDir(projectBaseDir).exists()) before marking bValid and assigning
pLogSettings->logFile so project-relative targets are not rejected.
- Around line 511-519: The code path handling
ProjectFileDefinitions::cValueAxisTag currently ignores malformed numbers and
leaves pRegisterSettings->valueAxis at its default; update the parsing in
projectfilexmlparser.cpp so that when child.tagName() ==
ProjectFileDefinitions::cValueAxisTag and child.text().toUInt(&bOk) yields bOk
== false you report a parsing error (same mechanism used by the other numeric
fields) and abort parsing (e.g. set the parser error/emit an XML error
referencing the tag and return false/upstream failure) instead of silently
continuing; reference pRegisterSettings, child,
ProjectFileDefinitions::cValueAxisTag and the valueAxis member when implementing
the check.
- Around line 836-843: The code currently hardcodes adapterId = 0 and overwrites
every device; instead compute the actual new adapter index via auto
newAdapterIndex = pGeneralSettings->adapterList.size() - 1 after appending
adapterSettings and only assign that index/type to devices that are truly new or
uninitialized (e.g. whose deviceSettings[].adapterId is invalid/placeholder or
whose adapterType is empty) rather than iterating all entries; update the loop
that sets adapterId/adapterType to check deviceSettings[i].adapterId (or
adapterType) before modifying so existing devices are not rewritten and new
devices point to newAdapterIndex with adapterType = "modbus".
- Around line 671-685: The parser is using the system default locale (QLocale())
when converting persisted numeric strings, so decimals written with '.' can fail
on locales using ','; update the parsing to use the fixed C locale instead:
replace uses of QLocale().toDouble(...) when filling pYAxisSettings->scaleMin
and pYAxisSettings->scaleMax (and any similar conversions in this file) with
QLocale::c().toDouble(...), and keep the existing error reporting
(parseErr.reportError) behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3c1c5984-6205-451a-a398-66c166895208

📥 Commits

Reviewing files that changed from the base of the PR and between 0b3d910 and c931166.

📒 Files selected for processing (11)
  • src/communication/modbuspoll.cpp
  • src/dialogs/addregisterwidget.cpp
  • src/importexport/projectfiledefinitions.h
  • src/importexport/projectfilehandler.cpp
  • src/importexport/projectfilexmlparser.cpp
  • src/importexport/projectfilexmlparser.h
  • tests/importexport/CMakeLists.txt
  • tests/importexport/projectfilexmltestdata.cpp
  • tests/importexport/projectfilexmltestdata.h
  • tests/importexport/tst_projectfilexmlparser.cpp
  • tests/importexport/tst_projectfilexmlparser.h
💤 Files with no reviewable changes (1)
  • src/communication/modbuspoll.cpp

Comment on lines +671 to +685
pYAxisSettings->scaleMin = QLocale().toDouble(child.text(), &bRet);
if (bRet)
{
bMin = true;
}
else
{
parseErr.reportError(
QString("Scale (y-axis) has an incorrect minimum. \"%1\" is not a valid double").arg(child.text()));
break;
}
}
else if (child.tagName() == ProjectFileDefinitions::cMaxTag)
{
pYAxisSettings->scaleMax = QLocale().toDouble(child.text(), &bRet);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's read the specific file and lines mentioned
cat -n src/importexport/projectfilexmlparser.cpp | sed -n '665,690p'

Repository: ModbusScope/NotOnlyModbusScope

Length of output: 1084


🏁 Script executed:

# Search for all QLocale() usages in the codebase to understand the scope
rg 'QLocale\(\)' --type cpp --type hpp -n

Repository: ModbusScope/NotOnlyModbusScope

Length of output: 101


🏁 Script executed:

# Search specifically for QLocale().toDouble patterns
rg 'QLocale\(\)\.toDouble' --type cpp --type hpp -n

Repository: ModbusScope/NotOnlyModbusScope

Length of output: 101


🏁 Script executed:

# Search for all QLocale() usages without file type restrictions
rg 'QLocale\(\)' -n

Repository: ModbusScope/NotOnlyModbusScope

Length of output: 2337


🏁 Script executed:

# Search for QLocale::c() usage to see if it's used elsewhere
rg 'QLocale::c' -n

Repository: ModbusScope/NotOnlyModbusScope

Length of output: 56


🏁 Script executed:

# Search for .toDouble patterns in general to understand parsing patterns
rg '\.toDouble\(' -n | head -30

Repository: ModbusScope/NotOnlyModbusScope

Length of output: 2232


🏁 Script executed:

# Check projectfilejsonparser.cpp for how it parses doubles
cat -n src/importexport/projectfilejsonparser.cpp | sed -n '465,475p'

Repository: ModbusScope/NotOnlyModbusScope

Length of output: 544


Use a fixed locale for parsing persisted project file data.

QLocale() uses the system's current locale, causing project files written with . decimals to fail or misparse on systems configured for , as the decimal separator. Project data stored on disk must be locale-independent.

Proposed fix
         if (child.tagName() == ProjectFileDefinitions::cMinTag)
         {
-            pYAxisSettings->scaleMin = QLocale().toDouble(child.text(), &bRet);
+            pYAxisSettings->scaleMin = QLocale::c().toDouble(child.text(), &bRet);
             if (bRet)
             {
                 bMin = true;
@@
         else if (child.tagName() == ProjectFileDefinitions::cMaxTag)
         {
-            pYAxisSettings->scaleMax = QLocale().toDouble(child.text(), &bRet);
+            pYAxisSettings->scaleMax = QLocale::c().toDouble(child.text(), &bRet);
             if (bRet)
             {
                 bMax = true;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pYAxisSettings->scaleMin = QLocale().toDouble(child.text(), &bRet);
if (bRet)
{
bMin = true;
}
else
{
parseErr.reportError(
QString("Scale (y-axis) has an incorrect minimum. \"%1\" is not a valid double").arg(child.text()));
break;
}
}
else if (child.tagName() == ProjectFileDefinitions::cMaxTag)
{
pYAxisSettings->scaleMax = QLocale().toDouble(child.text(), &bRet);
pYAxisSettings->scaleMin = QLocale::c().toDouble(child.text(), &bRet);
if (bRet)
{
bMin = true;
}
else
{
parseErr.reportError(
QString("Scale (y-axis) has an incorrect minimum. \"%1\" is not a valid double").arg(child.text()));
break;
}
}
else if (child.tagName() == ProjectFileDefinitions::cMaxTag)
{
pYAxisSettings->scaleMax = QLocale::c().toDouble(child.text(), &bRet);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/importexport/projectfilexmlparser.cpp` around lines 671 - 685, The parser
is using the system default locale (QLocale()) when converting persisted numeric
strings, so decimals written with '.' can fail on locales using ','; update the
parsing to use the fixed C locale instead: replace uses of
QLocale().toDouble(...) when filling pYAxisSettings->scaleMin and
pYAxisSettings->scaleMax (and any similar conversions in this file) with
QLocale::c().toDouble(...), and keep the existing error reporting
(parseErr.reportError) behavior unchanged.

- Resolve relative log-file paths against the project base directory
  instead of the process CWD; pass the base dir from the file handler
  through parseFile and use it in parseLogToFile
- Report a parse error (instead of silently ignoring) when <valueaxis>
  contains a non-numeric value, consistent with all other numeric fields
- Compute the real adapter index after appending rather than hardcoding
  0; centralise adapterType/adapterId assignment in buildAdapterSettings
  and guard with adapterType.isEmpty() so pre-linked devices are skipped

Adds tests: valueAxisInvalid, logFileRelativePath

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (3)
src/importexport/projectfilexmlparser.cpp (3)

338-356: ⚠️ Potential issue | 🟠 Major

Same 8-bit range check needed here.

Apply the same fix as in parseLegacyConnectionTag to validate that parsed values are ≤ 255 before casting to quint8.

Proposed fix
         else if (child.tagName() == ProjectFileDefinitions::cSlaveIdTag)
         {
-            slaveId = static_cast<quint8>(child.text().toUInt(&bRet));
-            if (!bRet)
+            const quint32 value = child.text().toUInt(&bRet);
+            if (!bRet || value > 255)
             {
                 parseErr.reportError(QString("Slave id ( %1 ) is not a valid number").arg(child.text()));
                 break;
             }
+            slaveId = static_cast<quint8>(value);
         }
         else if (child.tagName() == ProjectFileDefinitions::cConsecutiveMaxTag)
         {
-            consecutiveMax = static_cast<quint8>(child.text().toUInt(&bRet));
-            if (!bRet)
+            const quint32 value = child.text().toUInt(&bRet);
+            if (!bRet || value > 255)
             {
                 parseErr.reportError(
                   QString("Consecutive register maximum ( %1 ) is not a valid number").arg(child.text()));
                 break;
             }
+            consecutiveMax = static_cast<quint8>(value);
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/importexport/projectfilexmlparser.cpp` around lines 338 - 356, When
parsing child tags ProjectFileDefinitions::cSlaveIdTag and ::cConsecutiveMaxTag
in src/importexport/projectfilexmlparser.cpp (the code that assigns slaveId and
consecutiveMax), validate the parsed uint value is within 0..255 before casting
to quint8: call child.text().toUInt(&bRet), check bRet and that the resulting
uintValue <= 255, and only then static_cast to quint8; otherwise call
parseErr.reportError with the existing descriptive message and break. Match the
same numeric-range check logic used in parseLegacyConnectionTag to avoid
truncation when casting.

243-261: ⚠️ Potential issue | 🟠 Major

Reject out-of-range 8-bit values before narrowing.

toUInt() only validates that the text is a valid unsigned integer. Values like 300 pass validation but silently wrap when cast to quint8, causing slaveid or consecutivemax to contain incorrect values.

Proposed fix
         if (child.tagName() == ProjectFileDefinitions::cSlaveIdTag)
         {
-            slaveId = static_cast<quint8>(child.text().toUInt(&bRet));
-            if (!bRet)
+            const quint32 value = child.text().toUInt(&bRet);
+            if (!bRet || value > 255)
             {
                 parseErr.reportError(QString("Slave id ( %1 ) is not a valid number").arg(child.text()));
                 break;
             }
+            slaveId = static_cast<quint8>(value);
         }
         else if (child.tagName() == ProjectFileDefinitions::cConsecutiveMaxTag)
         {
-            consecutiveMax = static_cast<quint8>(child.text().toUInt(&bRet));
-            if (!bRet)
+            const quint32 value = child.text().toUInt(&bRet);
+            if (!bRet || value > 255)
             {
                 parseErr.reportError(
                   QString("Consecutive register maximum ( %1 ) is not a valid number").arg(child.text()));
                 break;
             }
+            consecutiveMax = static_cast<quint8>(value);
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/importexport/projectfilexmlparser.cpp` around lines 243 - 261, The
current parsing for ProjectFileDefinitions::cSlaveIdTag and ::cConsecutiveMaxTag
uses child.text().toUInt(&bRet) then static_cast<quint8>, which accepts values
like "300" and silently wraps; modify the parsing in projectfilexmlparser.cpp to
first parse to a larger unsigned (e.g. quint32 or unsigned int) with
toUInt(&bRet), then check bRet and ensure the parsed value is <= 0xFF before
assigning to slaveId or consecutiveMax; if out of range report the error via
parseErr.reportError (use the same human-friendly messages) and avoid the
cast/assignment when invalid.

678-705: ⚠️ Potential issue | 🟠 Major

Use a fixed locale for parsing persisted project file data.

QLocale() uses the system's current locale, causing project files written with . as the decimal separator to fail or misparse on systems configured for , (e.g., German, Dutch, French locales). Project data stored on disk must be locale-independent.

Proposed fix
         if (child.tagName() == ProjectFileDefinitions::cMinTag)
         {
-            pYAxisSettings->scaleMin = QLocale().toDouble(child.text(), &bRet);
+            pYAxisSettings->scaleMin = QLocale::c().toDouble(child.text(), &bRet);
             if (bRet)
             {
                 bMin = true;
             }
             // ...
         }
         else if (child.tagName() == ProjectFileDefinitions::cMaxTag)
         {
-            pYAxisSettings->scaleMax = QLocale().toDouble(child.text(), &bRet);
+            pYAxisSettings->scaleMax = QLocale::c().toDouble(child.text(), &bRet);
             if (bRet)
             {
                 bMax = true;
             }
             // ...
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/importexport/projectfilexmlparser.cpp` around lines 678 - 705, The parser
currently uses QLocale() (system locale) to parse persisted numeric strings for
pYAxisSettings->scaleMin and scaleMax (nodes ProjectFileDefinitions::cMinTag and
::cMaxTag), which breaks on systems using comma decimals; change
QLocale().toDouble(...) to use the fixed C locale via QLocale::c().toDouble(...)
for both scaleMin and scaleMax (and any other similar parsing sites in this
file) so project files are parsed locale-independently, keeping the same error
handling via parseErr.reportError when bRet is false.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/importexport/projectfilexmlparser.cpp`:
- Around line 338-356: When parsing child tags
ProjectFileDefinitions::cSlaveIdTag and ::cConsecutiveMaxTag in
src/importexport/projectfilexmlparser.cpp (the code that assigns slaveId and
consecutiveMax), validate the parsed uint value is within 0..255 before casting
to quint8: call child.text().toUInt(&bRet), check bRet and that the resulting
uintValue <= 255, and only then static_cast to quint8; otherwise call
parseErr.reportError with the existing descriptive message and break. Match the
same numeric-range check logic used in parseLegacyConnectionTag to avoid
truncation when casting.
- Around line 243-261: The current parsing for
ProjectFileDefinitions::cSlaveIdTag and ::cConsecutiveMaxTag uses
child.text().toUInt(&bRet) then static_cast<quint8>, which accepts values like
"300" and silently wraps; modify the parsing in projectfilexmlparser.cpp to
first parse to a larger unsigned (e.g. quint32 or unsigned int) with
toUInt(&bRet), then check bRet and ensure the parsed value is <= 0xFF before
assigning to slaveId or consecutiveMax; if out of range report the error via
parseErr.reportError (use the same human-friendly messages) and avoid the
cast/assignment when invalid.
- Around line 678-705: The parser currently uses QLocale() (system locale) to
parse persisted numeric strings for pYAxisSettings->scaleMin and scaleMax (nodes
ProjectFileDefinitions::cMinTag and ::cMaxTag), which breaks on systems using
comma decimals; change QLocale().toDouble(...) to use the fixed C locale via
QLocale::c().toDouble(...) for both scaleMin and scaleMax (and any other similar
parsing sites in this file) so project files are parsed locale-independently,
keeping the same error handling via parseErr.reportError when bRet is false.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ec76d696-0207-463f-b27d-2fdbf1c195fe

📥 Commits

Reviewing files that changed from the base of the PR and between c931166 and 2501d76.

📒 Files selected for processing (7)
  • src/importexport/projectfilehandler.cpp
  • src/importexport/projectfilexmlparser.cpp
  • src/importexport/projectfilexmlparser.h
  • tests/importexport/projectfilexmltestdata.cpp
  • tests/importexport/projectfilexmltestdata.h
  • tests/importexport/tst_projectfilexmlparser.cpp
  • tests/importexport/tst_projectfilexmlparser.h
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/importexport/projectfilexmltestdata.h
  • tests/importexport/projectfilexmltestdata.cpp
  • tests/importexport/tst_projectfilexmlparser.h

@jgeudens jgeudens merged commit b91feed into master Apr 13, 2026
10 checks passed
@jgeudens jgeudens deleted the claude/xml-to-json-migration-7Y842 branch April 13, 2026 18:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants