• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

antonvw / wex / 20691103835

04 Jan 2026 09:49AM UTC coverage: 64.291% (+0.001%) from 64.29%
20691103835

push

github

web-flow
1141 improve unified diff (#1142)

* improve unified_diff

* initialize members

* simplified parse

18529 of 31593 branches covered (58.65%)

Branch coverage included in aggregate %.

14754 of 20176 relevant lines covered (73.13%)

1515.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.74
/src/factory/unified-diff-parser.cpp
1
////////////////////////////////////////////////////////////////////////////////
2
// Name:      unified-diff-parser.cpp
3
// Purpose:   Implementation of unified_diff_parser
4
//            https://www.gnu.org/software/diffutils/manual/html_node/Detailed-Unified.html
5
// Author:    Anton van Wezenbeek
6
// Copyright: (c) 2025-2026 Anton van Wezenbeek
7
////////////////////////////////////////////////////////////////////////////////
8

9
#include <boost/parser/parser.hpp>
10
#include <wex/factory/unified-diff.h>
11

12
#include "unified-diff-parser.h"
13

14
namespace bp = boost::parser;
15

16
wex::factory::unified_diff_parser::unified_diff_parser(unified_diff* diff)
14✔
17
  : m_diff(diff)
14✔
18
{
19
  m_diff->m_range.fill({0});
14✔
20
  m_diff->m_diffs    = 0;
14✔
21
  m_diff->m_is_first = true;
14✔
22
  m_diff->m_is_last  = true;
14✔
23
  m_diff->m_type     = unified_diff::diff_t::UNKNOWN;
14✔
24
}
14✔
25

26
bool wex::factory::unified_diff_parser::parse()
14✔
27
{
28
  auto const action_diff = [this](const auto& ctx)
14✔
29
  {
30
    const auto tpl    = _attr(ctx);
14✔
31
    m_diff->m_path[0] = wex::path(std::get<0>(tpl));
14✔
32
    m_diff->m_path[1] = wex::path(std::get<1>(tpl));
14✔
33
    m_diff->m_range.fill({0});
14✔
34

35
    for (const auto& hunk : std::get<2>(tpl))
34✔
36
    {
37
      int index = 0;
20✔
38

39
      m_diff->m_is_first = (hunk == *std::get<2>(tpl).begin());
20✔
40
      m_diff->m_is_last  = (hunk == std::get<2>(tpl).back());
20✔
41

42
      for (const auto& number : std::get<0>(hunk))
60✔
43
      {
44
        if (const auto* val = std::get_if<std::tuple<int, int>>(&number); val)
40✔
45
        {
46
          const int range            = std::get<1>(*val);
14✔
47
          m_diff->m_range[index]     = std::abs(std::get<0>(*val));
14✔
48
          m_diff->m_range[index + 1] = range;
14✔
49

50
          if (range > 0)
14✔
51
          {
52
            m_diff->m_diffs++;
4✔
53
          }
54
        }
55
        else
56
        {
57
          m_diff->m_range[index]     = std::abs(std::get<int>(number));
26✔
58
          m_diff->m_range[index + 1] = 1;
26✔
59
          m_diff->m_diffs++;
26✔
60
        }
61

62
        index += 2;
40✔
63
      }
64

65
      m_diff->m_text.fill({});
20✔
66

67
      for (const auto& line : std::get<1>(hunk))
68✔
68
      {
69
        auto fix(line);
48✔
70

71
        if (line.starts_with("\n"))
48✔
72
        {
73
          fix = fix.substr(1);
8✔
74
        }
75

76
        switch (fix[0])
48!
77
        {
78
          case '+':
13✔
79
            m_diff->m_text[1].push_back(fix.substr(1));
13✔
80
            break;
13✔
81
          case '-':
20✔
82
            m_diff->m_text[0].push_back(fix.substr(1));
20✔
83
            break;
20✔
84
          case ' ':
×
85
            m_diff->m_text[0].push_back(fix.substr(1));
×
86
            m_diff->m_text[1].push_back(fix.substr(1));
×
87
            break;
×
88
        }
89
      }
90

91
      m_diff->m_type =
20✔
92
        (m_diff->m_type == unified_diff::diff_t::UNKNOWN ?
20✔
93
           unified_diff::diff_t::FIRST :
94
           unified_diff::diff_t::OTHER);
95

96
      m_diff->report_diff();
20✔
97
      m_diff->trace("found");
60✔
98
    }
99
  };
28✔
100

101
  auto const action_eoi = [this](const auto& ctx)
10✔
102
  {
103
    m_diff->m_type = unified_diff::diff_t::LAST;
10✔
104
    m_diff->report_diff_finish();
10✔
105
    m_diff->trace("finish");
18✔
106
  };
23✔
107

108
  // (Skip the first lines)
109
  // The unified output format starts with a two-line header:
110
  // --- from-file from-file-modification-time
111
  // +++ to-file to-file-modification-time
112
  // Next come one or more hunks of differences:
113
  // @@ from-file-line-numbers to-file-line-numbers @@
114
  // line-from-either-file
115
  // line-from-either-file...
116

117
  auto const parser_diff_lines = bp::lexeme[+(
14✔
118
    bp::char_ >> +(bp::char_ - bp::eol - "--- a/" - "@@" - "diff --"))];
119

120
  auto const parser_hunk =
121
    bp::lit("@@") >> bp::repeat(2)[bp::int_ >> ',' >> bp::int_ | bp::int_] >>
14✔
122
    bp::lit("@@") >> bp::lexeme[+(bp::char_ - bp::eol)] >> parser_diff_lines;
14✔
123

124
  auto const parser_diff = bp::lit("--- a/") >> +(bp::char_ - "+++ b/") >>
14✔
125
                           bp::lit("+++ b/") >> +(bp::char_ - "@@") >>
14✔
126
                           +parser_hunk;
14✔
127

128
  auto const parser_skip = bp::omit[*(bp::char_ - "--- a/")];
14✔
129

130
  auto const parser_all =
131
    *(parser_skip >> +parser_diff[action_diff]) >> bp::eoi[action_eoi];
14✔
132

133
  return bp::parse(m_diff->input(), parser_all, bp::ws);
27✔
134
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc