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

antonvw / wex / 26363148583

24 May 2026 01:54PM UTC coverage: 64.86% (+0.009%) from 64.851%
26363148583

push

github

web-flow
clang-tidy updates (#1264)

18899 of 31968 branches covered (59.12%)

Branch coverage included in aggregate %.

15101 of 20453 relevant lines covered (73.83%)

1530.47 hits per line

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

67.4
/src/ex/variable.cpp
1
////////////////////////////////////////////////////////////////////////////////
2
// Name:      variable.cpp
3
// Purpose:   Implementation of class wex::variable
4
// Author:    Anton van Wezenbeek
5
// Copyright: (c) 2018-2026 Anton van Wezenbeek
6
////////////////////////////////////////////////////////////////////////////////
7

8
#include <wex/core/chrono.h>
9
#include <wex/core/log.h>
10
#include <wex/core/regex.h>
11
#include <wex/ex/ex.h>
12
#include <wex/ex/macro-mode.h>
13
#include <wex/ex/macros.h>
14
#include <wex/ex/variable.h>
15
#include <wex/factory/process.h>
16
#include <wex/syntax/stc.h>
17
#include <wex/ui/frame.h>
18
#include <wx/app.h>
19

20
#include <utility>
21

22
// Several types of variables are supported.
23
// See xml file.
24
enum class wex::variable::input_t
25
{
26
  BUILTIN,     // a builtin variable like "Created"
27
  ENVIRONMENT, // an environment variable like ENV
28
  FIXED,       // fixed value from macros xml file
29
  INPUT,       // input always from user
30
  INPUT_ONCE,  // input once from user when starting, save value in xml file
31
  INPUT_ONCE_EMPTY, // input once from user if value is empty, save value in xml
32
                    // file
33
  INPUT_SAVE,       // input always from user, save value in xml file
34
  PROCESS,          // value is output from contents as a runnable process
35
  TEMPLATE          // read value from a template file
36
};
37

38
wex::variable::variable(std::string name)
13✔
39
  : m_name(std::move(name))
13✔
40
  , m_type(input_t::INPUT_SAVE)
26✔
41
{
42
}
13✔
43

44
wex::variable::variable(const pugi::xml_node& node)
1,104✔
45
  : m_name(node.attribute("name").value())
1,104✔
46
  , m_type(input_t::INPUT_SAVE)
1,104✔
47
  , m_value(node.text().get())
1,104✔
48
  , m_format(node.attribute("format").value())
1,104✔
49
  , m_prefix(node.attribute("prefix").value())
3,312✔
50
{
51
  if (const std::string type = node.attribute("type").value(); !type.empty())
1,104!
52
  {
53
    if (type == "BUILTIN")
1,104✔
54
    {
55
      m_type = input_t::BUILTIN;
647✔
56
    }
57
    else if (type == "ENVIRONMENT")
457✔
58
    {
59
      m_type = input_t::ENVIRONMENT;
91✔
60
    }
61
    else if (type == "FIXED")
366✔
62
    {
63
      m_type = input_t::FIXED;
1✔
64
    }
65
    else if (type == "INPUT")
365✔
66
    {
67
      m_type = input_t::INPUT;
1✔
68
    }
69
    else if (type == "INPUT-SAVE")
364✔
70
    {
71
      m_type = input_t::INPUT_SAVE;
91✔
72
    }
73
    else if (type == "INPUT-ONCE")
273✔
74
    {
75
      m_type = input_t::INPUT_ONCE;
45✔
76
    }
77
    else if (type == "INPUT-ONCE-EMPTY")
228✔
78
    {
79
      m_type = input_t::INPUT_ONCE_EMPTY;
135✔
80
    }
81
    else if (type == "PROCESS")
93✔
82
    {
83
      m_type = input_t::PROCESS;
46✔
84
    }
85
    else if (type == "TEMPLATE")
47✔
86
    {
87
      m_type = input_t::TEMPLATE;
46✔
88
    }
89
    else
90
    {
91
      log("variable") << m_name << "type:" << type << "unknown";
2✔
92
    }
93
  }
1,104✔
94
}
1,104✔
95

96
bool wex::variable::check_link(std::string& value) const
115✔
97
{
98
  if (regex v(regex_valid_names()); v.match(m_value) == 3)
115!
99
  {
100
    if (
×
101
      const auto& it = ex::get_macros().get_variables().find(v[1]);
×
102
      it != ex::get_macros().get_variables().end())
×
103
    {
104
      if (!it->second.expand(value))
×
105
      {
106
        if (!is_input())
×
107
        {
108
          log("variable") << m_name << "(" << v[0] << ") could not be expanded";
×
109
        }
110
      }
111
      else
112
      {
113
        if (!value.empty())
×
114
        {
115
          log::trace("variable")
×
116
            << m_name << "(" << v[0] << ") expanded:" << value;
×
117
          return true;
×
118
        }
119
      }
120
    }
121
    else
122
    {
123
      log("variable") << m_name << "(" << v[0] << ") is not found";
×
124
    }
125
  }
115!
126

127
  return false;
115✔
128
}
129

130
bool wex::variable::expand(ex* ex)
49✔
131
{
132
  std::string value;
49✔
133

134
  if (check_link(value))
49!
135
  {
136
    m_value = value;
×
137
    value.clear();
×
138
  }
139

140
  if (!expand(value, ex))
49✔
141
  {
142
    if (!is_input())
1!
143
    // Now only show log status if this is no input variable,
144
    // as it was cancelled in that case.
145
    {
146
      log::status(_("Could not expand variable")) << m_name;
1✔
147
    }
148

149
    return false;
1✔
150
  }
151

152
  // Add to stc component, if present.
153
  if (ex != nullptr && ex->get_stc() != nullptr)
48!
154
  {
155
    if (ex->get_stc()->GetReadOnly() || ex->get_stc()->is_hexmode())
47!
156
    {
157
      return false;
×
158
    }
159

160
    auto commented(value);
47✔
161

162
    // If there is a prefix, make a comment out of it.
163
    if (!m_prefix.empty())
47!
164
    {
165
      commented = ex->get_stc()->get_lexer().make_comment(
×
166
        m_prefix == "WRAP" ? std::string() : m_prefix,
×
167
        value);
×
168
    }
169

170
    ex->get_stc()->add_text(commented);
47✔
171
  }
47✔
172

173
  m_value = value;
48✔
174

175
  log::trace("variable") << m_name << "expanded to:" << value;
48✔
176

177
  if (
48✔
178
    (m_type == input_t::INPUT_ONCE_EMPTY && !m_value.empty()) ||
96!
179
    m_type == input_t::INPUT_ONCE)
48!
180
  {
181
    m_ask_for_input = false;
×
182
  }
183

184
  return true;
48✔
185
}
49✔
186

187
bool wex::variable::expand(std::string& value, ex* ex) const
66✔
188
{
189
  check_link(value);
66✔
190

191
  switch (m_type)
66!
192
  {
193
    case input_t::BUILTIN:
53✔
194
      if (!expand_builtin(ex, value))
53!
195
      {
196
        return false;
×
197
      }
198
      break;
53✔
199

200
    case input_t::ENVIRONMENT:
4✔
201
      if (wxString val; !wxGetEnv(m_name, &val))
4!
202
      {
203
        return false;
×
204
      }
205
      else
206
      {
207
        value = val;
4✔
208
      }
4!
209
      break;
4✔
210

211
    case input_t::FIXED:
1✔
212
      if (m_value.empty())
1!
213
      {
214
        return false;
×
215
      }
216
      value = m_value;
1✔
217
      break;
1✔
218

219
    case input_t::INPUT:
6✔
220
    case input_t::INPUT_ONCE:
221
    case input_t::INPUT_ONCE_EMPTY:
222
    case input_t::INPUT_SAVE:
223
      if (!expand_input(value))
6!
224
      {
225
        return false;
×
226
      }
227
      break;
6✔
228

229
    case input_t::PROCESS:
1✔
230
      if (m_value.empty())
1!
231
      {
232
        return false;
×
233
      }
234

235
      if (
1✔
236
        factory::process p;
1✔
237
        p.system(
3!
238
          m_value + (!m_argument.empty() ? " " + m_argument : std::string())) !=
2!
239
        0)
240
      {
241
        return false;
×
242
      }
243
      /* NOLINTNEXTLINE */
244
      else
245
      {
246
        value = p.std_out();
1✔
247
        m_argument.clear();
1✔
248
      }
1!
249
      break;
1✔
250

251
    case input_t::TEMPLATE:
1✔
252
      if (!ex::get_macros().mode().expand(ex, *this, value))
1!
253
      {
254
        return false;
1✔
255
      }
256
      break;
×
257

258
    default:
×
259
      assert(0);
×
260
      break;
261
  }
262

263
  return true;
65✔
264
}
265

266
bool wex::variable::expand_builtin(ex* ex, std::string& expanded) const
53✔
267
{
268
  if (m_name == "Date")
53✔
269
  {
270
    expanded = (m_format.empty() ? now("%Y-%m-%d") : now(m_format));
13!
271
  }
272
  else if (m_name == "Datetime")
48✔
273
  {
274
    expanded = (m_format.empty() ? now("%Y-%m-%d %H:%M:%S") : now(m_format));
9!
275
  }
276
  else if (m_name == "Time")
45✔
277
  {
278
    expanded = (m_format.empty() ? now("%H:%M:%S") : now(m_format));
9!
279
  }
280
  else if (m_name == "Year")
42✔
281
  {
282
    expanded = (m_format.empty() ? now("%Y") : now(m_format));
19!
283
  }
284
  else if (ex != nullptr)
35!
285
  {
286
    if (m_name == "Cb")
35✔
287
    {
288
      expanded = ex->get_stc()->get_lexer().comment_begin();
4✔
289
    }
290
    else if (m_name == "Cc")
31✔
291
    {
292
      const int line     = ex->get_stc()->get_current_line();
3✔
293
      const int startPos = ex->get_stc()->PositionFromLine(line);
3✔
294
      const int endPos   = ex->get_stc()->GetLineEndPosition(line);
3✔
295
      expanded           = ex->get_stc()->get_lexer().comment_complete(
9✔
296
        ex->get_stc()->GetTextRange(startPos, endPos).ToStdString());
9✔
297
    }
298
    else if (m_name == "Ce")
28✔
299
    {
300
      expanded = ex->get_stc()->get_lexer().comment_end();
4✔
301
    }
302
    else if (m_name == "Cl")
24✔
303
    {
304
      expanded = ex->get_stc()->get_lexer().make_comment(std::string(), false);
4✔
305
    }
306
    else if (m_name == "Created")
20✔
307
    {
308
      if (
4!
309
        path file(ex->get_stc()->path()); ex->get_stc()->path().stat().is_ok())
4✔
310
      {
311
        expanded =
312
          (m_format.empty() ? file.stat().get_creation_time_str() :
8!
313
                              file.stat().get_creation_time_str(m_format));
4!
314
      }
315
      else
316
      {
317
        expanded = (m_format.empty() ? now("%Y-%m-%d") : now(m_format));
×
318
      }
4✔
319
    }
320
    else if (m_name == "Filename")
16✔
321
    {
322
      expanded = ex->get_stc()->path().name();
3✔
323
    }
324
    else if (m_name == "Fullname")
13✔
325
    {
326
      expanded = ex->get_stc()->path().filename();
3✔
327
    }
328
    else if (m_name == "Fullpath")
10✔
329
    {
330
      expanded = ex->get_stc()->path().string();
3✔
331
    }
332
    else if (m_name == "Nl")
7✔
333
    {
334
      expanded = ex->get_stc()->eol();
4✔
335
    }
336
    else if (m_name == "Path")
3!
337
    {
338
      expanded = ex->get_stc()->path().parent_path();
3✔
339
    }
340
    else
341
    {
342
      return false;
×
343
    }
344
  }
345

346
  return true;
53✔
347
}
348

349
bool wex::variable::expand_input(std::string& expanded) const
6✔
350
{
351
  auto* frame = dynamic_cast<wex::frame*>(wxTheApp->GetTopWindow());
6!
352

353
  if (frame->stc_entry_dialog_component() == nullptr)
6!
354
  {
355
    expanded = m_value;
6✔
356
    return true;
6✔
357
  }
358

359
  if (m_ask_for_input)
×
360
  {
361
    const auto use(!expanded.empty() ? expanded : m_value);
×
362

363
    frame->stc_entry_dialog_title(m_name);
×
364
    frame->stc_entry_dialog_component()->SetWrapMode(wxSTC_WRAP_WORD);
×
365
    frame->stc_entry_dialog_component()->set_text(use);
×
366
    frame->stc_entry_dialog_component()->SetFocus();
×
367

368
    bool ended = false;
×
369

370
    if (wxIsBusy())
×
371
    {
372
      ended = true;
×
373
      wxEndBusyCursor();
×
374
    }
375

376
    const int result = frame->stc_entry_dialog_show(true);
×
377

378
    if (ended)
×
379
    {
380
      wxBeginBusyCursor();
×
381
    }
382

383
    if (result == wxID_CANCEL)
×
384
    {
385
      return false;
×
386
    }
387

388
    const auto& value(frame->stc_entry_dialog_component()->get_text());
×
389

390
    if (value.empty())
×
391
    {
392
      return false;
×
393
    }
394

395
    expanded = value;
×
396
  }
×
397
  else
398
  {
399
    expanded = m_value;
×
400
  }
401

402
  return true;
×
403
}
404

405
bool wex::variable::is_builtin() const
66✔
406
{
407
  return m_type == input_t::BUILTIN;
66✔
408
}
409

410
bool wex::variable::is_input() const
291✔
411
{
412
  return m_type == input_t::INPUT || m_type == input_t::INPUT_ONCE ||
291✔
413
         m_type == input_t::INPUT_ONCE_EMPTY || m_type == input_t::INPUT_SAVE;
582!
414
}
415

416
bool wex::variable::is_template() const
5✔
417
{
418
  return m_type == input_t::TEMPLATE;
5✔
419
}
420

421
std::string wex::variable::regex_valid_names()
5,244✔
422
{
423
  return "(@)([a-zA-Z][a-zA-Z0-9:]+)(@)";
10,488✔
424
}
425

426
void wex::variable::save(pugi::xml_node& node, const std::string* value)
30✔
427
{
428
  assert(!m_name.empty());
30!
429

430
  if (!node.attribute("name"))
30✔
431
  {
432
    node.append_attribute("name") = m_name;
8✔
433
  }
434

435
  if (!node.attribute("type"))
30✔
436
  {
437
    pugi::xml_attribute type = node.append_attribute("type");
8✔
438

439
    switch (m_type)
8!
440
    {
441
      case input_t::BUILTIN:
1✔
442
        type.set_value("BUILTIN");
1✔
443
        break;
1✔
444
      case input_t::ENVIRONMENT:
1✔
445
        type.set_value("ENVIRONMENT");
1✔
446
        break;
1✔
447
      case input_t::FIXED:
1✔
448
        type.set_value("FIXED");
1✔
449
        break;
1✔
450
      case input_t::INPUT:
1✔
451
        type.set_value("INPUT");
1✔
452
        break;
1✔
453
      case input_t::INPUT_ONCE:
×
454
        type.set_value("INPUT-ONCE");
×
455
        break;
×
456
      case input_t::INPUT_ONCE_EMPTY:
×
457
        type.set_value("INPUT-ONCE-EMPTY");
×
458
        break;
×
459
      case input_t::INPUT_SAVE:
2✔
460
        type.set_value("INPUT-SAVE");
2✔
461
        break;
2✔
462
      case input_t::PROCESS:
1✔
463
        type.set_value("PROCESS");
1✔
464
        break;
1✔
465
      case input_t::TEMPLATE:
1✔
466
        type.set_value("TEMPLATE");
1✔
467
        break;
1✔
468

469
      default:
×
470
        assert(0);
×
471
        break;
472
    }
473
  }
474

475
  if (!m_prefix.empty() && !node.attribute("prefix"))
30!
476
  {
477
    node.append_attribute("prefix") = m_prefix;
×
478
  }
479

480
  if (value != nullptr)
30!
481
  {
482
    m_value = *value;
×
483
  }
484

485
  if (
30✔
486
    !m_value.empty() && m_type != input_t::BUILTIN &&
53✔
487
    m_type != input_t::INPUT && m_type != input_t::PROCESS)
53✔
488
  {
489
    node.text().set(m_value);
6✔
490
  }
491
}
30✔
492

493
void wex::variable::set_argument(const std::string& val)
1✔
494
{
495
  m_argument = val;
1✔
496

497
  log::trace("variable") << "argument:" << m_argument;
1✔
498
}
1✔
499

500
void wex::variable::set_ask_for_input(bool value)
319✔
501
{
502
  if (!value || is_input() && m_type != input_t::INPUT_ONCE_EMPTY)
319✔
503
  {
504
    m_ask_for_input = value;
67✔
505
  }
506
}
319✔
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