The Battle for Wesnoth  1.19.0-dev
lua_interpreter.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2024
3  by Chris Beck <render787@gmail.com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #define GETTEXT_DOMAIN "wesnoth-lib"
17 
19 
21 #include "gui/widgets/button.hpp"
22 #include "gui/widgets/label.hpp"
24 #include "gui/widgets/text_box.hpp"
25 #include "gui/widgets/window.hpp"
26 
27 #include "desktop/clipboard.hpp"
28 #include "game_config.hpp"
29 #include "game_errors.hpp"
30 #include "gettext.hpp"
31 #include "play_controller.hpp"
32 #include "resources.hpp" //for help fetching lua kernel pointers
33 #include "scripting/plugins/manager.hpp" //needed for the WHICH_KERNEL version of display
34 #include "scripting/game_lua_kernel.hpp" //needed for the WHICH_KERNEL version of display
37 #include "log.hpp"
38 #include "font/pango/escape.hpp"
39 
40 #include <sstream>
41 #include <string>
42 #include <vector>
43 #include <functional>
44 
45 #ifdef HAVE_HISTORY
46 #include "filesystem.hpp"
47 #include <readline/history.h>
48 #endif
49 
50 static lg::log_domain log_lua_int("lua/interpreter");
51 #define DBG_LUA LOG_STREAM(debug, log_lua_int)
52 #define LOG_LUA LOG_STREAM(info, log_lua_int)
53 #define WRN_LUA LOG_STREAM(warn, log_lua_int)
54 #define ERR_LUA LOG_STREAM(err, log_lua_int)
55 
56 namespace gui2::dialogs
57 {
58 
59 REGISTER_DIALOG(lua_interpreter)
60 
61 // Model, View, Controller definitions
62 
64 private:
65  scroll_label* msg_label; //the view is extremely simple, it's pretty much just this one widget that gets updated
67 
68 public:
69  view() : msg_label(nullptr), window_(nullptr) {}
70 
71  /** Bind the scroll label widget to my pointer, and configure */
72  void bind(window& window) {
73  window_ = &window;
74  msg_label = find_widget<scroll_label>(&window, "msg", false, true);
75  msg_label->set_use_markup(true);
77  msg_label->set_label("");
78  }
79 
80  /** Update the scroll label contents */
81  void update_contents(const std::string & str)
82  {
83  assert(msg_label);
84  msg_label->set_label(str);
86  }
87 
88  void pg_up()
89  {
90  assert(msg_label);
92  }
93 
94  void pg_down()
95  {
96  assert(msg_label);
98  }
99 };
100 
101 /**
102  * The lua model is responsible to interact with the lua kernel base and keep track of what should be displayed in the console.
103  * It registers its stringstream with the lua kernel when it is created, and unregisters when it is destroyed.
104  *
105  * It is responsible to execute commands as strings, or add dialog messages for the user. It is also responsible to ask
106  * the lua kernel for help with tab completion.
107  */
109 private:
111  std::stringstream log_;
112  std::stringstream raw_log_;
113 
114 public:
116  : L_(lk)
117  , log_()
118  , raw_log_()
119  {
120  DBG_LUA << "constructing a lua_interpreter::model";
121  //DBG_LUA << "incoming:\n" << lk.get_log().rdbuf() << "\n.";
122  log_ << font::escape_text(lk.get_log().str()) << std::flush;
123  raw_log_ << lk.get_log().str() << std::flush;
124  // Lua kernel sends log strings to this function
125  L_.set_external_log([this](const std::string & str) {
126  log_ << font::escape_text(str);
127  raw_log_ << str;
128  });
129  //DBG_LUA << "received:\n" << log_.str() << "\n.";
130 
131  DBG_LUA << "finished constructing a lua_interpreter::model";
132  }
133 
135  {
136  DBG_LUA << "destroying a lua_interpreter::model";
137  L_.set_external_log(nullptr); //deregister our log since it's about to be destroyed
138  }
139 
140  /** Ask the lua kernel to execute a command. No throw of game::lua_error, instead the error message is formatted and printed to console.*/
141  bool execute(const std::string & cmd);
142 
143  /** Add a message from the dialog, formatted in blue to distinguish from issued commands.
144  * This message gets put in the interpreter log, but does not get entered in the kernel log, so if the window is closed this message will
145  * not appear the next time it is opened.
146  **/
147  void add_dialog_message(const std::string & msg);
148 
149  /** Get the log string */
150  std::string get_log() const { return log_.str(); }
151  /** Get the unescaped log */
152  std::string get_raw_log() const { return raw_log_.str(); }
153  /** Get a string describing the name of lua kernel */
154  std::string get_name() const { return L_.my_name(); }
155 
156  /** Clear the console log */
157  void clear_log() {
158  L_.clear_log();
159  log_.str("");
160  log_.clear();
161  raw_log_.str("");
162  raw_log_.clear();
163  }
164 
165  //* Tab completion: Get list of presently defined global variables */
166  std::vector<std::string> get_globals() { return L_.get_global_var_names(); }
167  //* Tab completion: Get list of attributes for variable corresponding to this path. */
168  std::vector<std::string> get_attribute_names(const std::string & s) { return L_.get_attribute_names(s); }
169 };
170 
171 /**
172  * The input_model keeps track of what commands were executed before, and figures out what
173  * should be displayed when the user presses up / down arrows in the input.
174  * It is essentially part of the model, but it isn't connected to the lua kernel so I have implemented it
175  * separately. Putatively it could all be refactored so that there is a single model with private subclass "lua_model"
176  * and also a "command_history_model" but I have decided simply to not implement it that way.
177  */
179 private:
180  std::string prefix_;
182 #ifdef HAVE_HISTORY
183  std::string filename_;
184 #endif
185 
186 public:
188  : prefix_()
189  , end_of_history_(true)
190 #ifdef HAVE_HISTORY
191  , filename_(filesystem::get_user_config_dir() + "/lua_command_history")
192  {
193  using_history();
194  read_history (filename_.c_str());
195  }
196 #else
197  {}
198 #endif
199 
200 #ifdef HAVE_HISTORY
201  ~input_model()
202  {
203  try {
204  const std::size_t history_max = 500;
206  append_history (history_max,filename_.c_str());
207  } else {
208  write_history (filename_.c_str());
209  }
210 
211  history_truncate_file (filename_.c_str(), history_max);
212  } catch (...) { PLAIN_LOG << "Swallowed an exception when trying to write lua command line history";}
213  }
214 #endif
215  void add_to_history ([[maybe_unused]] const std::string& str) {
216  prefix_ = "";
217 #ifdef HAVE_HISTORY
218  add_history(str.c_str());
219 #endif
220  end_of_history_ = true;
221 
222  }
223 
224  void maybe_update_prefix (const std::string & text) {
225  LOG_LUA << "maybe update prefix";
226  LOG_LUA << "prefix_: '"<< prefix_ << "'\t text='"<< text << "'";
227 
228  if (!end_of_history_) return;
229 
230  prefix_ = text;
231  LOG_LUA << "updated prefix";
232  }
233 
234  std::string search([[maybe_unused]] int direction ) {
235 #ifdef HAVE_HISTORY
236  LOG_LUA << "searching in direction " << direction << " from position " << where_history();
237 
238  HIST_ENTRY * e = nullptr;
239  if (end_of_history_) {
240  // if the direction is > 0, do nothing because searching down only takes place when we are in the history records.
241  if (direction < 0) {
242  history_set_pos(history_length);
243 
244  if (prefix_.size() > 0) {
245  int result = history_search_prefix(prefix_.c_str(), direction);
246  if (result == 0) {
247  e = current_history();
248  }
249  } else {
250  e = previous_history();
251  }
252  }
253  } else {
254  e = (direction > 0) ? next_history() : previous_history();
255  if (prefix_.size() > 0 && e) {
256  int result = history_search_prefix(prefix_.c_str(), direction);
257  if (result == 0) {
258  e = current_history();
259  } else {
260  e = nullptr; // if the search misses, it leaves the state as it was, which might not have been on an entry matching prefix.
261  end_of_history_ = true; // we actually want to force it to be null and treat as off the end of history in this case.
262  }
263  }
264  }
265 
266  if (e) {
267  LOG_LUA << "found something at " << where_history();
268  std::string ret = e->line;
269  end_of_history_ = false;
270  return ret;
271  }
272 #endif
273 
274  LOG_LUA << "didn't find anything";
275 
276  // reset, set history to the end and prefix_ to empty, and return the current prefix_ for the user to edit
277  end_of_history_ = true;
278  std::string temp = prefix_;
279  prefix_ = "";
280  return temp;
281  }
282 
283  std::string clear_history() {
284 #ifdef HAVE_HISTORY
285  ::clear_history();
286  write_history (filename_.c_str());
287  return "Cleared history.";
288 #else
289  return "History is disabled, you did not compile with GNU history support.";
290 #endif
291  }
292 
293  std::string list_history() {
294 #ifdef HAVE_HISTORY
295  HIST_ENTRY **the_list;
296 
297  the_list = history_list ();
298  if (the_list) {
299  if (!*the_list) {
300  return "History is empty.";
301  }
302 
303  std::string result;
304  for (int i = 0; the_list[i]; i++) {
305  result += std::to_string(i+history_base);
306  result += ": ";
307  result += the_list[i]->line;
308  result += "\n";
309  }
310  return result;
311  } else {
312  return "Couldn't find history.";
313  }
314 #else
315  return "History is disabled, you did not compile with GNU history support.";
316 #endif
317  }
318 };
319 
320 /**
321  * The controller is responsible to hold all the input widgets, and a pointer to the model and view.
322  * It is responsible to bind the input signals to appropriate handler methods, which it holds.
323  * It is also responsible to ask the view to update based on the output of the model, typically in
324  * response to some input.
325  */
327 private:
330 
332  std::string text_entry_;
333 
334  const std::unique_ptr<lua_interpreter::lua_model> lua_model_;
335  const std::unique_ptr<lua_interpreter::input_model> input_model_;
336  const std::unique_ptr<lua_interpreter::view> view_;
337 
338  void execute();
339  void tab();
340  void search(int direction);
341 public:
343  : copy_button(nullptr)
344  , clear_button(nullptr)
345  , text_entry(nullptr)
346  , text_entry_()
349  , view_(new lua_interpreter::view())
350  {}
351 
352  /** Bind my pointers to the widgets found in the window */
353  void bind(window& window);
354 
357 
358  void input_keypress_callback(bool& handled,
359  bool& halt,
360  const SDL_Keycode key,
361  window& window);
362 
363  /** Update the view based on the model */
364  void update_view();
365 
366  friend class lua_interpreter;
367 };
368 
369 // Model impl
370 
371 /** Execute a command, and report any errors encountered. */
372 bool lua_interpreter::lua_model::execute (const std::string & cmd)
373 {
374  LOG_LUA << "lua_interpreter::model::execute...";
375 
376  try {
377  L_.interactive_run(cmd.c_str());
378  return true;
379  } catch (const game::lua_error & e) {
380  add_dialog_message(std::string(e.what()));
381  return false;
382  }
383 }
384 
385 /** Add a dialog message, which will appear in blue. */
387  log_ << "<span color='#8888FF'>" << font::escape_text(msg) << "</span>\n";
388  raw_log_ << msg << '\n';
389 }
390 
391 // View impl
392 
393 // Controller impl
394 
395 /** Update the view based on the model. */
397 {
398  LOG_LUA << "lua_interpreter update_view...";
399  assert(lua_model_);
400  assert(view_);
401 
402  view_->update_contents(lua_model_->get_log());
403 
404  LOG_LUA << "lua_interpreter update_view finished";
405 }
406 
407 /** Find all the widgets managed by the controller and connect them to handler methods. */
409 {
410  LOG_LUA << "Entering lua_interpreter::controller::bind";
411  assert(view_);
412  view_->bind(window);
413 
414  text_entry = find_widget<text_box>(&window, "text_entry", false, true);
415  //text_entry->set_text_changed_callback(
416  // std::bind(&view::filter, this, std::ref(window)));
417  window.keyboard_capture(text_entry);
418  window.set_click_dismiss(false);
420 
422  *text_entry,
424  this,
425  std::placeholders::_3,
426  std::placeholders::_4,
427  std::placeholders::_5,
428  std::ref(window)));
429 
430  copy_button = find_widget<button>(&window, "copy", false, true);
432  *copy_button,
434  this,
435  std::ref(window)));
436 
437  clear_button = find_widget<button>(&window, "clear", false, true);
439  *clear_button,
441  this,
442  std::ref(window)));
443 
445  copy_button->set_active(false);
446  copy_button->set_tooltip(_("Clipboard support not found, contact your packager"));
447  }
448 
449  LOG_LUA << "Exiting lua_interpreter::controller::bind";
450 }
451 
452 /** Copy text to the clipboard */
454 {
455  assert(lua_model_);
456  desktop::clipboard::copy_to_clipboard(lua_model_->get_raw_log(), false);
457 }
458 
459 /** Clear the text */
461 {
462  assert(lua_model_);
463  lua_model_->clear_log();
464  assert(view_);
465  view_->update_contents("");
466 }
467 
468 /** Handle return key (execute) or tab key (tab completion) */
470  bool& halt,
471  const SDL_Keycode key,
472  window& window)
473 {
474  assert(lua_model_);
475  assert(text_entry);
476 
477  LOG_LUA << "keypress_callback";
478  if(key == SDLK_RETURN || key == SDLK_KP_ENTER) { // handle executing whatever is in the command entry field
479  LOG_LUA << "executing...";
480  execute();
481  handled = true;
482  halt = true;
483 
484  // Commands such as `wesnoth.interface.zoom` might cause the display to redraw and leave the window half-drawn.
485  // This preempts that.
487 
488  LOG_LUA << "finished executing";
489  } else if(key == SDLK_TAB) { // handle tab completion
490  tab();
491  handled = true;
492  halt = true;
493  } else if(key == SDLK_UP) {
494  search(-1);
495  handled = true;
496  halt = true;
497  } else if(key == SDLK_DOWN) {
498  search(1);
499  handled = true;
500  halt = true;
501  } else if(key == SDLK_PAGEUP) {
502  view_->pg_up();
503  handled = true;
504  halt = true;
505  } else if(key == SDLK_PAGEDOWN) {
506  view_->pg_down();
507  handled = true;
508  halt = true;
509  }
510 }
511 
513 {
514  std::string cmd = text_entry->get_value();
515  if (cmd.empty()) return; //don't bother with empty string
516 
517  cmd.erase(cmd.find_last_not_of(" \n\r\t")+1); //right trim the string
518 
519  LOG_LUA << "Executing '"<< cmd << "'";
520 
521  if (cmd.size() >= 13 && (cmd.substr(0,13) == "history clear" || cmd.substr(0,13) == "clear history")) {
522  lua_model_->add_dialog_message(input_model_->clear_history());
523  text_entry->set_value("");
524  update_view();
525  return;
526  }
527 
528  if (cmd.size() >= 7 && (cmd.substr(0,7) == "history")) {
529  lua_model_->add_dialog_message(input_model_->list_history());
530  text_entry->set_value("");
531  update_view();
532  return;
533  }
534 
535  if (lua_model_->execute(cmd)) {
536  input_model_->add_to_history(cmd);
537  text_entry->set_value("");
538  }
539  update_view();
540 }
541 
543 {
544  std::string text = text_entry->get_value();
545 
546  std::string prefix;
547  std::size_t prefix_end_pos = text.find_last_of(" (");
548  if (prefix_end_pos != std::string::npos) {
549  prefix = text.substr(0, prefix_end_pos + 1);
550  text = text.substr(prefix_end_pos + 1);
551  }
552 
553  static std::vector<std::string> static_matches {
554  "and",
555  "break",
556  "else",
557  "elseif",
558  "end",
559  "false",
560  "for",
561  "function",
562  "local",
563  "nil",
564  "not",
565  "repeat",
566  "return",
567  "then",
568  "true",
569  "until",
570  "while"
571  };
572 
573  std::vector<std::string> matches;
574 
575  if (text.find('.') == std::string::npos) {
576  matches = lua_model_->get_globals();
577  matches.insert(matches.end(), static_matches.begin(), static_matches.end());
578  } else {
579  matches = lua_model_->get_attribute_names(text);
580  }
581 
582  //bool line_start = utils::word_completion(text, matches);
583  if (text.size() > 0) { // this if is to avoid weird behavior in word_completion, where it thinks nothing matches the empty string
584  utils::word_completion(text, matches);
585  }
586 
587  if(matches.empty()) {
588  return;
589  }
590 
591  //if(matches.size() == 1) {
592  //text.append(" "); //line_start ? ": " : " ");
593  //} else {
594  if (matches.size() > 1) {
595  //std::string completion_list = utils::join(matches, " ");
596 
597  const std::size_t wrap_limit = 80;
598  std::string buffer;
599 
600  for (std::size_t idx = 0; idx < matches.size(); ++idx) {
601  if (buffer.size() + 1 + matches.at(idx).size() > wrap_limit) {
602  lua_model_->add_dialog_message(buffer);
603  buffer = matches.at(idx);
604  } else {
605  if (buffer.size()) {
606  buffer += (" " + matches.at(idx));
607  } else {
608  buffer = matches.at(idx);
609  }
610  }
611  }
612 
613  lua_model_->add_dialog_message(buffer);
614  update_view();
615  }
616  text_entry->set_value(prefix + text);
617 }
618 
620 {
621  std::string current_text = text_entry->get_value();
622  input_model_->maybe_update_prefix(current_text);
623  text_entry->set_value(input_model_->search(direction));
624 
625 #ifndef HAVE_HISTORY
626  lua_model_->add_dialog_message("History is disabled, you did not compile with GNU history support.");
627  update_view();
628 #endif
629 
630 }
631 
632 // Dialog implementation
633 
634 /** Display a new console, using given video and lua kernel */
636 #ifndef ALWAYS_HAVE_LUA_CONSOLE
639  const std::string& message = _("The lua console can only be used in debug mode! (Run ':debug' first)");
640  chat_man.add_chat_message(time(nullptr), _("lua console"), 0, message, events::chat_handler::MESSAGE_PRIVATE, false);
641  return;
642  }
643 #endif
644  if (!lk) {
645  ERR_LUA << "Tried to open console with a null lua kernel pointer.";
646  return;
647  }
648 
649  lua_interpreter(*lk).show();
650 }
651 
652 /** Helper function to assist those callers which don't want to include resources.hpp */
654  if (which == lua_interpreter::APP) {
655  display(plugins_manager::get()->get_kernel_base());
656  } else if (which == lua_interpreter::GAME) {
658  }
659 }
660 
661 /** Bind the controller, initialize one of the static labels with info about this kernel, and update the view. */
663 {
664  LOG_LUA << "Entering lua_interpreter::view::pre_show";
665  register_text("text_entry", false, controller_->text_entry_, true);
666  controller_->bind(window);
667 
668  label *kernel_type_label = find_widget<label>(&window, "kernel_type", false, true);
669  kernel_type_label->set_label(controller_->lua_model_->get_name());
670 
671  controller_->update_view();
672  //window.invalidate_layout(); // workaround for assertion failure
673  LOG_LUA << "Exiting lua_interpreter::view::pre_show";
674 }
675 
679 {
680  LOG_LUA << "entering lua_interpreter ctor...";
681  LOG_LUA << "finished lua_interpreter ctor...";
682 }
683 
684 } // namespace dialogs
std::string filename_
Definition: action_wml.cpp:538
void add_chat_message(const std::time_t &time, const std::string &speaker, int side, const std::string &msg, events::chat_handler::MESSAGE_TYPE type, bool bell)
display_chat_manager & get_chat_manager()
Simple push button.
Definition: button.hpp:36
The controller is responsible to hold all the input widgets, and a pointer to the model and view.
void handle_clear_button_clicked(window &window)
Clear the text.
void handle_copy_button_clicked(window &window)
Copy text to the clipboard.
void update_view()
Update the view based on the model.
const std::unique_ptr< lua_interpreter::view > view_
void input_keypress_callback(bool &handled, bool &halt, const SDL_Keycode key, window &window)
Handle return key (execute) or tab key (tab completion)
const std::unique_ptr< lua_interpreter::lua_model > lua_model_
void bind(window &window)
Bind my pointers to the widgets found in the window.
const std::unique_ptr< lua_interpreter::input_model > input_model_
The input_model keeps track of what commands were executed before, and figures out what should be dis...
void add_to_history([[maybe_unused]] const std::string &str)
void maybe_update_prefix(const std::string &text)
std::string search([[maybe_unused]] int direction)
The lua model is responsible to interact with the lua kernel base and keep track of what should be di...
void clear_log()
Clear the console log.
bool execute(const std::string &cmd)
Ask the lua kernel to execute a command.
std::vector< std::string > get_globals()
std::string get_log() const
Get the log string.
std::string get_name() const
Get a string describing the name of lua kernel.
void add_dialog_message(const std::string &msg)
Add a message from the dialog, formatted in blue to distinguish from issued commands.
std::string get_raw_log() const
Get the unescaped log.
std::vector< std::string > get_attribute_names(const std::string &s)
void bind(window &window)
Bind the scroll label widget to my pointer, and configure.
void update_contents(const std::string &str)
Update the scroll label contents.
static void display(lua_kernel_base *lk)
Display a new console, using given video and lua kernel.
virtual const std::string & window_id() const override
The ID of the window to build.
const std::unique_ptr< controller > controller_
lua_interpreter(lua_kernel_base &lk)
virtual void pre_show(window &window) override
Bind the controller, initialize one of the static labels with info about this kernel,...
Main class to show messages to the user.
Definition: message.hpp:36
Abstract base class for all modal dialogs.
field_text * register_text(const std::string &id, const bool mandatory, const std::function< std::string()> callback_load_value=nullptr, const std::function< void(const std::string &)> callback_save_value=nullptr, const bool capture_focus=false)
Creates a new text field.
A label displays text that can be wrapped but no scrollbars are provided.
Definition: label.hpp:56
Label showing a text.
virtual void set_label(const t_string &label) override
See styled_widget::set_label.
virtual void set_use_markup(bool use_markup) override
See styled_widget::set_use_markup.
@ END
Go to the end position.
Definition: scrollbar.hpp:58
@ JUMP_BACKWARDS
Go the visible items towards the begin.
Definition: scrollbar.hpp:57
void scroll_vertical_scrollbar(const scrollbar_base::scroll_mode scroll)
Scrolls the vertical scrollbar.
@ ALWAYS_VISIBLE
The scrollbar is always shown, whether needed or not.
void set_vertical_scrollbar_mode(const scrollbar_mode scrollbar_mode)
virtual void set_label(const t_string &text)
Class for a single line text area.
Definition: text_box.hpp:142
void queue_redraw()
Indicates that this widget should be redrawn.
Definition: widget.cpp:455
base class of top level items, the only item which needs to store the final canvases to draw on.
Definition: window.hpp:63
void set_enter_disabled(const bool enter_disabled)
Disable the enter key.
Definition: window.hpp:327
void keyboard_capture(widget *widget)
Definition: window.cpp:1221
void set_click_dismiss(const bool click_dismiss)
Definition: window.hpp:416
void set_external_log(external_log_type lg)
std::vector< std::string > get_global_var_names()
Get tab completion strings.
void interactive_run(char const *prog)
Tests if a program resolves to an expression, and pretty prints it if it is, otherwise it runs it nor...
std::vector< std::string > get_attribute_names(const std::string &var_path)
Gets all attribute names of an extended variable name.
const std::stringstream & get_log()
Access / manipulate logging of lua kernel activities.
virtual std::string my_name()
User-visible name of the lua kernel that they are talking to.
game_display & get_display() override
Get a reference to a display member a derived class uses.
static plugins_manager * get()
Definition: manager.cpp:58
Declarations for File-IO.
std::size_t i
Definition: function.cpp:968
static std::string _(const char *str)
Definition: gettext.hpp:93
This file contains the window object, this object is a top level container which has the event manage...
Standard logging facilities (interface).
#define PLAIN_LOG
Definition: log.hpp:295
static lg::log_domain log_lua_int("lua/interpreter")
#define ERR_LUA
#define LOG_LUA
#define DBG_LUA
void copy_to_clipboard(const std::string &text, const bool)
Copies text to the clipboard.
Definition: clipboard.cpp:32
bool available()
Whether wesnoth was compiled with support for a clipboard.
Definition: clipboard.cpp:53
std::string get_user_config_dir()
Definition: filesystem.cpp:842
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:319
std::string escape_text(const std::string &text)
Escapes the pango markup characters in a text.
Definition: escape.hpp:33
const bool & debug
Definition: game_config.cpp:91
REGISTER_DIALOG(editor_edit_unit)
void connect_signal_pre_key_press(dispatcher &dispatcher, const signal_keyboard &signal)
Connects the signal for 'snooping' on the keypress.
Definition: dispatcher.cpp:172
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:177
game_lua_kernel * lua_kernel
Definition: resources.cpp:25
play_controller * controller
Definition: resources.cpp:21
bool word_completion(std::string &text, std::vector< std::string > &wordlist)
Try to complete the last word of 'text' with the 'wordlist'.
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
static std::string flush(std::ostringstream &s)
Definition: reports.cpp:89
Error used to report an error in a lua script or in the lua interpreter.
Definition: game_errors.hpp:54
static map_location::DIRECTION s
#define e