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

antonvw / wex / 23084300327

14 Mar 2026 08:29AM UTC coverage: 64.122% (-0.03%) from 64.153%
23084300327

push

github

web-flow
Merge to master (#1190)

* started with 26.04, added more const to chrono (#1012)

* 1013 add regex option to git grep (#1014)

* added regex option to git grep

* fixed lint error

* simplify get_branch (#1017)

* log_none respects trace level logging (#1019)

* 1020 allow skip micro and revision number (#1021)

* allow explicitly excluding micro and revision number

* mention change

* update version dialog as well

* 1015 make msw debug artefact for feature branches (#1022)

* use debug build for feature branches, and add sample as artefact

* added setting bevOverwrite

* use older version

* use github variables

* added quotes

* fix name

* use github.ref_name

* added script to get build config, use in ci-msw.yml

* remove trailing whitespace

* fixed compile error (?)

* updated condition

* updated condition

* added allow-failure

* use continue on error

* 1023 add url for open group base specs (#1024)

* added url for open group base specs

* fxing lint

* improve opening links (#1026)

* 1027 cmdline improve (#1028)

* cmdline improvement on options

* updated option for version

* use more std::ranges::contains (#1030)

* improve regex to find paths in lines (#1032)

* moved get_abbreviations from test library to test ex (#1034)

* use ProcessPendingEvents instead of wxYield (#1037)

* fix git admin dir in tests (#1039)

* 1035 fix crash on windows for git show (#1040)

* reenabled the execute-show test for msw, and added mayfail flag

* some test updates

* reenabled other windows tests for wex::vcs_entry

* upgrade boost version, clearified names

* try fixing crash, in debug mode it is OK

* 1041 fix hypertext auto complete (#1043)

* fixed hypertext complete, upgrade boost on ci ubuntu

* fix linter error

* fix ranges::all_of warning (#1045)

* fix recording inc macro, by using control k for it (#1046)

* removed K accelerator (#1048)

* 1049 no auto in public interface (#1050)

* no auto in public interface

* no auto in public in... (continued)

18596 of 31859 branches covered (58.37%)

Branch coverage included in aggregate %.

14825 of 20262 relevant lines covered (73.17%)

1490.48 hits per line

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

59.91
/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-2025 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 from user
30
  INPUT_ONCE,  // input once from user, save value in xml file
31
  INPUT_SAVE,  // input from user, save value in xml file
32
  PROCESS,     // value is output from contents as a runnable process
33
  TEMPLATE     // read value from a template file
34
};
35

36
wex::variable::variable(std::string name)
8✔
37
  : m_name(std::move(name))
8✔
38
  , m_type(input_t::INPUT_SAVE)
16✔
39
{
40
}
8✔
41

42
wex::variable::variable(const pugi::xml_node& node)
953✔
43
  : m_name(node.attribute("name").value())
953✔
44
  , m_type(input_t::INPUT_SAVE)
953✔
45
  , m_value(node.text().get())
953✔
46
  , m_format(node.attribute("format").value())
953✔
47
  , m_prefix(node.attribute("prefix").value())
2,859✔
48
{
49
  if (const std::string type = node.attribute("type").value(); !type.empty())
953!
50
  {
51
    if (type == "BUILTIN")
953✔
52
    {
53
      m_type = input_t::BUILTIN;
577✔
54
    }
55
    else if (type == "ENVIRONMENT")
376✔
56
    {
57
      m_type = input_t::ENVIRONMENT;
83✔
58
    }
59
    else if (type == "FIXED")
293✔
60
    {
61
      m_type = input_t::FIXED;
1✔
62
    }
63
    else if (type == "INPUT")
292✔
64
    {
65
      m_type = input_t::INPUT;
1✔
66
    }
67
    else if (type == "INPUT-SAVE")
291✔
68
    {
69
      m_type = input_t::INPUT_SAVE;
83✔
70
    }
71
    else if (type == "INPUT-ONCE")
208✔
72
    {
73
      m_type = input_t::INPUT_ONCE;
123✔
74
    }
75
    else if (type == "PROCESS")
85✔
76
    {
77
      m_type = input_t::PROCESS;
42✔
78
    }
79
    else if (type == "TEMPLATE")
43✔
80
    {
81
      m_type = input_t::TEMPLATE;
42✔
82
    }
83
    else
84
    {
85
      log("variable") << m_name << "type:" << type << "unknown";
2✔
86
    }
87
  }
953✔
88
}
953✔
89

90
bool wex::variable::check_link(std::string& value) const
39✔
91
{
92
  if (regex v("@([a-zA-Z].+)@"); v.match(m_value) > 0)
78!
93
  {
94
    if (const auto& it = ex::get_macros().get_variables().find(v[0]);
×
95
        it != ex::get_macros().get_variables().end())
×
96
    {
97
      if (!it->second.expand(value))
×
98
      {
99
        if (!is_input())
×
100
        {
101
          log("variable") << m_name << "(" << v[0] << ") could not be expanded";
×
102
        }
103
      }
104
      else
105
      {
106
        if (!value.empty())
×
107
        {
108
          log::trace("variable")
×
109
            << m_name << "(" << v[0] << ") expanded:" << value;
×
110
          return true;
×
111
        }
112
      }
113
    }
114
    else
115
    {
116
      log("variable") << m_name << "(" << v[0] << ") is not found";
×
117
    }
118
  }
39!
119

120
  return false;
39✔
121
}
122

123
bool wex::variable::expand(ex* ex)
18✔
124
{
125
  std::string value;
18✔
126

127
  if (check_link(value))
18!
128
  {
129
    m_value = value;
×
130
    value.clear();
×
131
  }
132

133
  if (!expand(value, ex))
18✔
134
  {
135
    if (!is_input())
1!
136
    // Now only show log status if this is no input variable,
137
    // as it was cancelled in that case.
138
    {
139
      log::status(_("Could not expand variable")) << m_name;
1✔
140
    }
141

142
    return false;
1✔
143
  }
144

145
  // If there is a prefix, make a comment out of it.
146
  auto commented(value);
17✔
147

148
  if (ex != nullptr && ex->get_stc() != nullptr)
17!
149
  {
150
    if (ex->get_stc()->GetReadOnly() || ex->get_stc()->is_hexmode())
16!
151
    {
152
      return false;
2✔
153
    }
154

155
    if (!m_prefix.empty())
14!
156
    {
157
      commented = ex->get_stc()->get_lexer().make_comment(
×
158
        m_prefix == "WRAP" ? std::string() : m_prefix,
×
159
        value);
×
160
    }
161

162
    ex->get_stc()->add_text(commented);
14✔
163
  }
164

165
  if (m_type == input_t::INPUT_SAVE || m_type == input_t::INPUT_ONCE)
15!
166
  {
167
    m_value = value;
4✔
168

169
    log::trace("variable") << m_name << "expanded and saved:" << m_value;
8✔
170
  }
171
  else
172
  {
173
    log::trace("variable") << m_name << "expanded to:" << value;
22✔
174
  }
175

176
  if (m_type == input_t::INPUT_ONCE && !m_value.empty())
15!
177
  {
178
    m_ask_for_input = false;
×
179
  }
180

181
  return true;
15✔
182
}
18✔
183

184
bool wex::variable::expand(std::string& value, ex* ex) const
21✔
185
{
186
  check_link(value);
21✔
187

188
  switch (m_type)
21!
189
  {
190
    case input_t::BUILTIN:
9✔
191
      if (!expand_builtin(ex, value))
9!
192
      {
193
        return false;
×
194
      }
195
      break;
9✔
196

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

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

216
    case input_t::INPUT:
6✔
217
    case input_t::INPUT_ONCE:
218
    case input_t::INPUT_SAVE:
219
      if (!expand_input(value))
6!
220
      {
221
        return false;
×
222
      }
223
      break;
6✔
224

225
    case input_t::PROCESS:
1✔
226
      if (m_value.empty())
1!
227
      {
228
        return false;
×
229
      }
230

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

246
    case input_t::TEMPLATE:
1✔
247
      if (!ex::get_macros().mode().expand(ex, *this, value))
1!
248
      {
249
        return false;
1✔
250
      }
251
      break;
×
252

253
    default:
×
254
      assert(0);
×
255
      break;
256
  }
257

258
  return true;
20✔
259
}
260

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

341
  return true;
9✔
342
}
343

344
bool wex::variable::expand_input(std::string& expanded) const
6✔
345
{
346
  auto* frame = dynamic_cast<wex::frame*>(wxTheApp->GetTopWindow());
6!
347

348
  if (frame->stc_entry_dialog_component() == nullptr)
6!
349
  {
350
    expanded = m_value;
6✔
351
    return true;
6✔
352
  }
353

354
  if (m_ask_for_input)
×
355
  {
356
    const auto use(!expanded.empty() ? expanded : m_value);
×
357

358
    frame->stc_entry_dialog_title(m_name);
×
359
    frame->stc_entry_dialog_component()->SetWrapMode(wxSTC_WRAP_WORD);
×
360
    frame->stc_entry_dialog_component()->set_text(use);
×
361
    frame->stc_entry_dialog_component()->SetFocus();
×
362

363
    bool ended = false;
×
364

365
    if (wxIsBusy())
×
366
    {
367
      ended = true;
×
368
      wxEndBusyCursor();
×
369
    }
370

371
    const int result = frame->stc_entry_dialog_show(true);
×
372

373
    if (ended)
×
374
    {
375
      wxBeginBusyCursor();
×
376
    }
377

378
    if (result == wxID_CANCEL)
×
379
    {
380
      return false;
×
381
    }
382

383
    const auto& value(frame->stc_entry_dialog_component()->get_text());
×
384

385
    if (value.empty())
×
386
    {
387
      return false;
×
388
    }
389

390
    expanded = value;
×
391
  }
×
392
  else
393
  {
394
    expanded = m_value;
×
395
  }
396

397
  return true;
×
398
}
399

400
bool wex::variable::is_builtin() const
3✔
401
{
402
  return m_type == input_t::BUILTIN;
3✔
403
}
404

405
bool wex::variable::is_input() const
279✔
406
{
407
  return m_type == input_t::INPUT || m_type == input_t::INPUT_ONCE ||
522!
408
         m_type == input_t::INPUT_SAVE;
522✔
409
}
410

411
bool wex::variable::is_template() const
5✔
412
{
413
  return m_type == input_t::TEMPLATE;
5✔
414
}
415

416
void wex::variable::save(pugi::xml_node& node, const std::string* value)
14✔
417
{
418
  assert(!m_name.empty());
14!
419

420
  if (!node.attribute("name"))
14✔
421
  {
422
    node.append_attribute("name") = m_name;
8✔
423
  }
424

425
  if (!node.attribute("type"))
14✔
426
  {
427
    pugi::xml_attribute type = node.append_attribute("type");
8✔
428

429
    switch (m_type)
8!
430
    {
431
      case input_t::BUILTIN:
1✔
432
        type.set_value("BUILTIN");
1✔
433
        break;
1✔
434
      case input_t::ENVIRONMENT:
1✔
435
        type.set_value("ENVIRONMENT");
1✔
436
        break;
1✔
437
      case input_t::FIXED:
1✔
438
        type.set_value("FIXED");
1✔
439
        break;
1✔
440
      case input_t::INPUT:
1✔
441
        type.set_value("INPUT");
1✔
442
        break;
1✔
443
      case input_t::INPUT_ONCE:
×
444
        type.set_value("INPUT-ONCE");
×
445
        break;
×
446
      case input_t::INPUT_SAVE:
2✔
447
        type.set_value("INPUT-SAVE");
2✔
448
        break;
2✔
449
      case input_t::PROCESS:
1✔
450
        type.set_value("PROCESS");
1✔
451
        break;
1✔
452
      case input_t::TEMPLATE:
1✔
453
        type.set_value("TEMPLATE");
1✔
454
        break;
1✔
455

456
      default:
×
457
        assert(0);
×
458
        break;
459
    }
460
  }
461

462
  if (!m_prefix.empty() && !node.attribute("prefix"))
14!
463
  {
464
    node.append_attribute("prefix") = m_prefix;
×
465
  }
466

467
  if (value != nullptr)
14!
468
  {
469
    m_value = *value;
×
470
  }
471

472
  if (
14✔
473
    !m_value.empty() && m_type != input_t::BUILTIN &&
19!
474
    m_type != input_t::INPUT && m_type != input_t::PROCESS)
19✔
475
  {
476
    node.text().set(m_value);
3✔
477
  }
478
}
14✔
479

480
void wex::variable::set_argument(const std::string& val)
1✔
481
{
482
  m_argument = val;
1✔
483

484
  log::trace("variable") << "argument:" << m_argument;
1✔
485
}
1✔
486

487
void wex::variable::set_ask_for_input(bool value)
291✔
488
{
489
  if (!value || is_input() && m_type != input_t::INPUT_ONCE)
291✔
490
  {
491
    m_ask_for_input = value;
39✔
492
  }
493
}
291✔
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