The Battle for Wesnoth  1.19.7+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 
20 #include "gui/widgets/button.hpp"
21 #include "gui/widgets/label.hpp"
23 #include "gui/widgets/text_box.hpp"
24 #include "gui/widgets/window.hpp"
25 
26 #include "desktop/clipboard.hpp"
27 #include "game_config.hpp"
28 #include "game_errors.hpp"
29 #include "gettext.hpp"
30 #include "play_controller.hpp"
31 #include "resources.hpp" //for help fetching lua kernel pointers
32 #include "scripting/plugins/manager.hpp" //needed for the WHICH_KERNEL version of display
33 #include "scripting/game_lua_kernel.hpp" //needed for the WHICH_KERNEL version of display
36 #include "serialization/markup.hpp"
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 = window_->find_widget<scroll_label>("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
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_ << markup::span_color("#8888FF", font::escape_text(msg)) << "\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 = window.find_widget<text_box>("text_entry", false, true);
415  window.keyboard_capture(text_entry);
416  window.set_click_dismiss(false);
418 
420  *text_entry,
422  this,
423  std::placeholders::_3,
424  std::placeholders::_4,
425  std::placeholders::_5,
426  std::ref(window)));
427 
428  copy_button = window.find_widget<button>("copy", false, true);
430  *copy_button,
432 
433  clear_button = window.find_widget<button>("clear", false, true);
435  *clear_button,
437 
438  LOG_LUA << "Exiting lua_interpreter::controller::bind";
439 }
440 
441 /** Copy text to the clipboard */
443 {
444  assert(lua_model_);
445  desktop::clipboard::copy_to_clipboard(lua_model_->get_raw_log());
446 }
447 
448 /** Clear the text */
450 {
451  assert(lua_model_);
452  lua_model_->clear_log();
453  assert(view_);
454  view_->update_contents("");
455 }
456 
457 /** Handle return key (execute) or tab key (tab completion) */
459  bool& halt,
460  const SDL_Keycode key,
461  window& window)
462 {
463  assert(lua_model_);
464  assert(text_entry);
465 
466  LOG_LUA << "keypress_callback";
467  if(key == SDLK_RETURN || key == SDLK_KP_ENTER) { // handle executing whatever is in the command entry field
468  LOG_LUA << "executing...";
469  execute();
470  handled = true;
471  halt = true;
472 
473  // Commands such as `wesnoth.interface.zoom` might cause the display to redraw and leave the window half-drawn.
474  // This preempts that.
476 
477  LOG_LUA << "finished executing";
478  } else if(key == SDLK_TAB) { // handle tab completion
479  tab();
480  handled = true;
481  halt = true;
482  } else if(key == SDLK_UP) {
483  search(-1);
484  handled = true;
485  halt = true;
486  } else if(key == SDLK_DOWN) {
487  search(1);
488  handled = true;
489  halt = true;
490  } else if(key == SDLK_PAGEUP) {
491  view_->pg_up();
492  handled = true;
493  halt = true;
494  } else if(key == SDLK_PAGEDOWN) {
495  view_->pg_down();
496  handled = true;
497  halt = true;
498  }
499 }
500 
502 {
503  std::string cmd = text_entry->get_value();
504  if (cmd.empty()) return; //don't bother with empty string
505 
506  cmd.erase(cmd.find_last_not_of(" \n\r\t")+1); //right trim the string
507 
508  LOG_LUA << "Executing '"<< cmd << "'";
509 
510  if (cmd.size() >= 13 && (cmd.substr(0,13) == "history clear" || cmd.substr(0,13) == "clear history")) {
511  lua_model_->add_dialog_message(input_model_->clear_history());
512  text_entry->set_value("");
513  update_view();
514  return;
515  }
516 
517  if (cmd.size() >= 7 && (cmd.substr(0,7) == "history")) {
518  lua_model_->add_dialog_message(input_model_->list_history());
519  text_entry->set_value("");
520  update_view();
521  return;
522  }
523 
524  if (lua_model_->execute(cmd)) {
525  input_model_->add_to_history(cmd);
526  text_entry->set_value("");
527  }
528  update_view();
529 }
530 
532 {
533  std::string text = text_entry->get_value();
534 
535  std::string prefix;
536  std::size_t prefix_end_pos = text.find_last_of(" (");
537  if (prefix_end_pos != std::string::npos) {
538  prefix = text.substr(0, prefix_end_pos + 1);
539  text = text.substr(prefix_end_pos + 1);
540  }
541 
542  static std::vector<std::string> static_matches {
543  "and",
544  "break",
545  "else",
546  "elseif",
547  "end",
548  "false",
549  "for",
550  "function",
551  "local",
552  "nil",
553  "not",
554  "repeat",
555  "return",
556  "then",
557  "true",
558  "until",
559  "while"
560  };
561 
562  std::vector<std::string> matches;
563 
564  if (text.find('.') == std::string::npos) {
565  matches = lua_model_->get_globals();
566  matches.insert(matches.end(), static_matches.begin(), static_matches.end());
567  } else {
568  matches = lua_model_->get_attribute_names(text);
569  }
570 
571  //bool line_start = utils::word_completion(text, matches);
572  if (text.size() > 0) { // this if is to avoid weird behavior in word_completion, where it thinks nothing matches the empty string
573  utils::word_completion(text, matches);
574  }
575 
576  if(matches.empty()) {
577  return;
578  }
579 
580  //if(matches.size() == 1) {
581  //text.append(" "); //line_start ? ": " : " ");
582  //} else {
583  if (matches.size() > 1) {
584  //std::string completion_list = utils::join(matches, " ");
585 
586  const std::size_t wrap_limit = 80;
587  std::string buffer;
588 
589  for (std::size_t idx = 0; idx < matches.size(); ++idx) {
590  if (buffer.size() + 1 + matches.at(idx).size() > wrap_limit) {
591  lua_model_->add_dialog_message(buffer);
592  buffer = matches.at(idx);
593  } else {
594  if (buffer.size()) {
595  buffer += (" " + matches.at(idx));
596  } else {
597  buffer = matches.at(idx);
598  }
599  }
600  }
601 
602  lua_model_->add_dialog_message(buffer);
603  update_view();
604  }
605  text_entry->set_value(prefix + text);
606 }
607 
609 {
610  std::string current_text = text_entry->get_value();
611  input_model_->maybe_update_prefix(current_text);
612  text_entry->set_value(input_model_->search(direction));
613 
614 #ifndef HAVE_HISTORY
615  lua_model_->add_dialog_message("History is disabled, you did not compile with GNU history support.");
616  update_view();
617 #endif
618 
619 }
620 
621 // Dialog implementation
622 
623 /** Display a new console, using given video and lua kernel */
625 #ifndef ALWAYS_HAVE_LUA_CONSOLE
628  const std::string& message = _("The lua console can only be used in debug mode! (Run ‘:debug’ first)");
629  chat_man.add_chat_message(time(nullptr), _("lua console"), 0, message, events::chat_handler::MESSAGE_PRIVATE, false);
630  return;
631  }
632 #endif
633  if (!lk) {
634  ERR_LUA << "Tried to open console with a null lua kernel pointer.";
635  return;
636  }
637 
638  lua_interpreter(*lk).show();
639 }
640 
641 /** Helper function to assist those callers which don't want to include resources.hpp */
643  if (which == lua_interpreter::APP) {
644  display(plugins_manager::get()->get_kernel_base());
645  } else if (which == lua_interpreter::GAME) {
647  }
648 }
649 
650 /** Bind the controller, initialize one of the static labels with info about this kernel, and update the view. */
652 {
653  LOG_LUA << "Entering lua_interpreter::view::pre_show";
654  register_text("text_entry", false, controller_->text_entry_, true);
655  controller_->bind(*this);
656 
657  label *kernel_type_label = find_widget<label>("kernel_type", false, true);
658  kernel_type_label->set_label(controller_->lua_model_->get_name());
659 
660  controller_->update_view();
661  //window.invalidate_layout(); // workaround for assertion failure
662  LOG_LUA << "Exiting lua_interpreter::view::pre_show";
663 }
664 
668 {
669  LOG_LUA << "entering lua_interpreter ctor...";
670  LOG_LUA << "finished lua_interpreter ctor...";
671 }
672 
673 } // namespace dialogs
std::string filename_
Definition: action_wml.cpp:534
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_copy_button_clicked()
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.
virtual void pre_show() override
Bind the controller, initialize one of the static labels with info about this kernel,...
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)
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.
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)
A widget that allows the user to input text in single line.
Definition: text_box.hpp:125
void queue_redraw()
Indicates that this widget should be redrawn.
Definition: widget.cpp:464
T * find_widget(const std::string_view id, const bool must_be_active, const bool must_exist)
Gets a widget with the wanted id.
Definition: widget.hpp:753
base class of top level items, the only item which needs to store the final canvases to draw on.
Definition: window.hpp:61
void set_enter_disabled(const bool enter_disabled)
Disable the enter key.
Definition: window.hpp:322
void keyboard_capture(widget *widget)
Definition: window.cpp:1207
void set_click_dismiss(const bool click_dismiss)
Definition: window.hpp:411
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:1029
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:297
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)
Copies text to the clipboard.
Definition: clipboard.cpp:27
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:326
std::string get_lua_history_file()
std::string escape_text(std::string_view text)
Escapes the pango markup characters in a text.
Definition: escape.hpp:33
const bool & debug
Definition: game_config.cpp:94
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
std::string span_color(const color_t &color, Args &&... data)
Definition: markup.hpp:68
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:95
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