Merge branch 'staging/feature/wml-error-report-improvements'
This commit is contained in:
commit
37af8d2b08
12 changed files with 580 additions and 34 deletions
|
@ -96,6 +96,7 @@
|
|||
#enddef
|
||||
|
||||
{_GUI_DEFINITION "default" "default label" DEFAULT () DEFAULT ({GUI__TEXT_VERTICALLY_CENTRED})}
|
||||
{_GUI_DEFINITION "default_bold" "default label, bold font" DEFAULT "bold" DEFAULT ({GUI__TEXT_VERTICALLY_CENTRED})}
|
||||
{_GUI_DEFINITION "scroll_label" "scroll label" DEFAULT () DEFAULT 0}
|
||||
{_GUI_DEFINITION "title" "label used for titles" TITLE "bold" TITLE ({GUI__TEXT_VERTICALLY_CENTRED})}
|
||||
{_GUI_DEFINITION "default_large" "default, large font size" LARGE () DEFAULT ({GUI__TEXT_VERTICALLY_CENTRED})}
|
||||
|
|
173
data/gui/default/window/wml_error.cfg
Normal file
173
data/gui/default/window/wml_error.cfg
Normal file
|
@ -0,0 +1,173 @@
|
|||
#textdomain wesnoth-lib
|
||||
###
|
||||
### Dialog used to report WML parser or preprocessor errors.
|
||||
###
|
||||
|
||||
[window]
|
||||
id = "wml_error"
|
||||
description = "WML preprocessor/parser error report dialog."
|
||||
|
||||
[resolution]
|
||||
definition = "default"
|
||||
|
||||
#click_dismiss = "true"
|
||||
maximum_width = 600
|
||||
maximum_height = 600
|
||||
|
||||
automatic_placement = "true"
|
||||
vertical_placement = "center"
|
||||
horizontal_placement = "center"
|
||||
|
||||
[tooltip]
|
||||
id = "tooltip_large"
|
||||
[/tooltip]
|
||||
|
||||
[helptip]
|
||||
id = "tooltip_large"
|
||||
[/helptip]
|
||||
|
||||
[grid]
|
||||
|
||||
[row]
|
||||
|
||||
[column]
|
||||
border = "all"
|
||||
border_size = 5
|
||||
horizontal_alignment = "left"
|
||||
|
||||
[label]
|
||||
definition = "title"
|
||||
label = _ "Error"
|
||||
[/label]
|
||||
[/column]
|
||||
|
||||
[/row]
|
||||
|
||||
[row]
|
||||
|
||||
[column]
|
||||
border = "all"
|
||||
border_size = 5
|
||||
horizontal_alignment = "left"
|
||||
|
||||
[label]
|
||||
id = "summary"
|
||||
definition = "default"
|
||||
wrap = true
|
||||
[/label]
|
||||
[/column]
|
||||
|
||||
[/row]
|
||||
|
||||
[row]
|
||||
|
||||
[column]
|
||||
border = "all"
|
||||
border_size = 5
|
||||
horizontal_alignment = "left"
|
||||
|
||||
[label]
|
||||
id = "files"
|
||||
definition = "default"
|
||||
wrap = true
|
||||
[/label]
|
||||
[/column]
|
||||
|
||||
[/row]
|
||||
|
||||
[row]
|
||||
|
||||
[column]
|
||||
border = "all"
|
||||
border_size = 5
|
||||
horizontal_alignment = "left"
|
||||
|
||||
[label]
|
||||
id = "post_summary"
|
||||
definition = "default"
|
||||
wrap = true
|
||||
[/label]
|
||||
[/column]
|
||||
|
||||
[/row]
|
||||
|
||||
[row]
|
||||
|
||||
[column]
|
||||
horizontal_grow = "true"
|
||||
|
||||
[grid]
|
||||
|
||||
[row]
|
||||
|
||||
[column]
|
||||
border = "all"
|
||||
border_size = 5
|
||||
horizontal_alignment = "left"
|
||||
vertical_alignment = "bottom"
|
||||
|
||||
[label]
|
||||
id = "details_heading"
|
||||
definition = "default_bold"
|
||||
label = _ "Details:"
|
||||
[/label]
|
||||
[/column]
|
||||
|
||||
[column]
|
||||
border = "top,left,right"
|
||||
border_size = 5
|
||||
horizontal_alignment = "right"
|
||||
|
||||
[button]
|
||||
id = "copy"
|
||||
definition = "action_copy"
|
||||
label = _ "clipboard^Copy"
|
||||
tooltip = _ "Copy this report to clipboard"
|
||||
[/button]
|
||||
[/column]
|
||||
|
||||
[/row]
|
||||
|
||||
[/grid]
|
||||
|
||||
[/column]
|
||||
|
||||
[/row]
|
||||
|
||||
[row]
|
||||
|
||||
[column]
|
||||
border = "all"
|
||||
border_size = 5
|
||||
horizontal_grow = "true"
|
||||
|
||||
[scroll_label]
|
||||
id = "details"
|
||||
definition = "description"
|
||||
[/scroll_label]
|
||||
[/column]
|
||||
|
||||
[/row]
|
||||
|
||||
[row]
|
||||
|
||||
[column]
|
||||
border = "all"
|
||||
border_size = 5
|
||||
horizontal_alignment = "right"
|
||||
|
||||
[button]
|
||||
id = "ok"
|
||||
definition = "default"
|
||||
label = _ "OK"
|
||||
[/button]
|
||||
|
||||
[/column]
|
||||
|
||||
[/row]
|
||||
|
||||
[/grid]
|
||||
|
||||
[/resolution]
|
||||
|
||||
[/window]
|
|
@ -769,6 +769,7 @@ set(wesnoth-main_SRC
|
|||
gui/dialogs/transient_message.cpp
|
||||
gui/dialogs/unit_attack.cpp
|
||||
gui/dialogs/unit_create.cpp
|
||||
gui/dialogs/wml_error.cpp
|
||||
gui/dialogs/wml_message.cpp
|
||||
halo.cpp
|
||||
help.cpp
|
||||
|
|
|
@ -395,6 +395,7 @@ wesnoth_sources = Split("""
|
|||
gui/dialogs/transient_message.cpp
|
||||
gui/dialogs/unit_attack.cpp
|
||||
gui/dialogs/unit_create.cpp
|
||||
gui/dialogs/wml_error.cpp
|
||||
gui/dialogs/wml_message.cpp
|
||||
gui/lib/types/point.cpp
|
||||
gui/widgets/button.cpp
|
||||
|
|
|
@ -466,6 +466,10 @@ static int do_gameloop(int argc, char** argv)
|
|||
const cursor::manager cursor_manager;
|
||||
cursor::set(cursor::WAIT);
|
||||
|
||||
#if (defined(_X11) && !defined(__APPLE__)) || defined(_WIN32)
|
||||
SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
|
||||
#endif
|
||||
|
||||
loadscreen::global_loadscreen_manager loadscreen_manager(game->disp().video());
|
||||
|
||||
loadscreen::start_stage("init gui");
|
||||
|
@ -492,10 +496,6 @@ static int do_gameloop(int argc, char** argv)
|
|||
loadscreen::start_stage("refresh addons");
|
||||
refresh_addon_version_info_cache();
|
||||
|
||||
#if (defined(_X11) && !defined(__APPLE__)) || defined(_WIN32)
|
||||
SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
|
||||
#endif
|
||||
|
||||
config tips_of_day;
|
||||
|
||||
loadscreen::start_stage("titlescreen");
|
||||
|
|
|
@ -20,13 +20,14 @@
|
|||
#include "cursor.hpp"
|
||||
#include "game_config.hpp"
|
||||
#include "gettext.hpp"
|
||||
#include "gui/dialogs/message.hpp"
|
||||
#include "gui/dialogs/wml_error.hpp"
|
||||
#include "language.hpp"
|
||||
#include "loadscreen.hpp"
|
||||
#include "log.hpp"
|
||||
#include "resources.hpp"
|
||||
#include "scripting/lua.hpp"
|
||||
#include "hotkey/hotkey_item.hpp"
|
||||
#include "hotkey/hotkey_command.hpp"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
|
@ -174,10 +175,10 @@ void game_config_manager::load_game_config(FORCE_RELOAD_CONFIG force_reload,
|
|||
::init_strings(game_config());
|
||||
theme::set_known_themes(&game_config());
|
||||
} catch(game::error& e) {
|
||||
ERR_CONFIG << "Error loading game configuration files\n";
|
||||
gui2::show_error_message(disp_.video(),
|
||||
_("Error loading game configuration files: '") +
|
||||
e.message + _("' (The game will now exit)"));
|
||||
ERR_CONFIG << "Error loading game configuration files\n" << e.message << '\n';
|
||||
gui2::twml_error::display(
|
||||
_("Error loading game configuration files. The game will now exit."),
|
||||
e.message, disp_.video());
|
||||
throw;
|
||||
}
|
||||
|
||||
|
@ -198,7 +199,8 @@ void game_config_manager::load_addons_cfg()
|
|||
|
||||
get_files_in_dir(user_campaign_dir, &user_files, &user_dirs,
|
||||
ENTIRE_FILE_PATH);
|
||||
std::stringstream user_error_log;
|
||||
|
||||
std::vector<std::string> error_log;
|
||||
|
||||
// Append the $user_campaign_dir/*.cfg files to addons_to_load.
|
||||
BOOST_FOREACH(const std::string& uc, user_files) {
|
||||
|
@ -229,11 +231,11 @@ void game_config_manager::load_addons_cfg()
|
|||
ERR_CONFIG << "error reading usermade add-on '"
|
||||
<< file << "'\n";
|
||||
error_addons.push_back(file);
|
||||
user_error_log << "The format '~" << file.substr(userdata_loc)
|
||||
<< "' is only for single-file add-ons, use '~"
|
||||
<< file.substr(userdata_loc,
|
||||
error_log.push_back("The format '~" + file.substr(userdata_loc)
|
||||
+ "' is only for single-file add-ons, use '~"
|
||||
+ file.substr(userdata_loc,
|
||||
size_minus_extension - userdata_loc)
|
||||
<< "/_main.cfg' instead.\n";
|
||||
+ "/_main.cfg' instead.");
|
||||
}
|
||||
else {
|
||||
addons_to_load.push_back(file);
|
||||
|
@ -258,29 +260,34 @@ void game_config_manager::load_addons_cfg()
|
|||
game_config_.append(umc_cfg);
|
||||
} catch(config::error& err) {
|
||||
ERR_CONFIG << "error reading usermade add-on '" << uc << "'\n";
|
||||
ERR_CONFIG << err.message << '\n';
|
||||
error_addons.push_back(uc);
|
||||
user_error_log << err.message << "\n";
|
||||
error_log.push_back(err.message);
|
||||
} catch(preproc_config::error& err) {
|
||||
ERR_CONFIG << "error reading usermade add-on '" << uc << "'\n";
|
||||
ERR_CONFIG << err.message << '\n';
|
||||
error_addons.push_back(uc);
|
||||
user_error_log << err.message << "\n";
|
||||
error_log.push_back(err.message);
|
||||
} catch(io_exception&) {
|
||||
ERR_CONFIG << "error reading usermade add-on '" << uc << "'\n";
|
||||
error_addons.push_back(uc);
|
||||
}
|
||||
}
|
||||
if(error_addons.empty() == false) {
|
||||
std::stringstream msg;
|
||||
msg << _n("The following add-on had errors and could not be loaded:",
|
||||
"The following add-ons had errors and could not be loaded:",
|
||||
error_addons.size());
|
||||
BOOST_FOREACH(const std::string& error_addon, error_addons) {
|
||||
msg << "\n" << error_addon;
|
||||
}
|
||||
const size_t n = error_addons.size();
|
||||
const std::string& msg1 =
|
||||
_n("The following add-on had errors and could not be loaded:",
|
||||
"The following add-ons had errors and could not be loaded:",
|
||||
n);
|
||||
const std::string& msg2 =
|
||||
_n("Please report this to the author or maintainer of this add-on.",
|
||||
"Please report this to the respective authors or maintainers of these add-ons.",
|
||||
n);
|
||||
|
||||
msg << '\n' << _("ERROR DETAILS:") << '\n' << user_error_log.str();
|
||||
const std::string& report = utils::join(error_log, "\n\n");
|
||||
|
||||
gui2::show_error_message(disp_.video(),msg.str());
|
||||
gui2::twml_error::display(msg1, msg2, error_addons, report,
|
||||
disp_.video());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
236
src/gui/dialogs/wml_error.cpp
Normal file
236
src/gui/dialogs/wml_error.cpp
Normal file
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Ignacio R. Morelle <shadowm2006@gmail.com>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#define GETTEXT_DOMAIN "wesnoth-lib"
|
||||
|
||||
#include "gui/dialogs/wml_error.hpp"
|
||||
|
||||
#include "addon/info.hpp"
|
||||
#include "addon/manager.hpp"
|
||||
#include "clipboard.hpp"
|
||||
#include "filesystem.hpp"
|
||||
#include "gettext.hpp"
|
||||
#include "gui/auxiliary/find_widget.tpp"
|
||||
#include "gui/widgets/button.hpp"
|
||||
#include "gui/widgets/control.hpp"
|
||||
#include "gui/widgets/settings.hpp"
|
||||
#include "gui/widgets/window.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
inline bool is_dir_separator(char c)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return c == '/' || c == '\\';
|
||||
#else
|
||||
return c == '/';
|
||||
#endif
|
||||
}
|
||||
|
||||
void strip_trailing_dir_separators(std::string& str)
|
||||
{
|
||||
while(is_dir_separator(str[str.size() - 1])) {
|
||||
str.erase(str.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
std::string format_file_list(const std::vector<std::string>& files_original)
|
||||
{
|
||||
if(files_original.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const std::string& addons_path = get_addon_campaigns_dir();
|
||||
std::vector<std::string> files(files_original);
|
||||
|
||||
BOOST_FOREACH(std::string& file, files)
|
||||
{
|
||||
std::string base;
|
||||
std::string filename = file_name(file);
|
||||
std::string parent_path;
|
||||
|
||||
const bool is_main_cfg = filename == "_main.cfg";
|
||||
|
||||
if(is_main_cfg) {
|
||||
parent_path = directory_name(file) + "/..";
|
||||
} else {
|
||||
parent_path = directory_name(file);
|
||||
}
|
||||
|
||||
// Only proceed to pretty-format the filename if it's from the add-ons
|
||||
// directory.
|
||||
if(normalize_path(parent_path) != normalize_path(addons_path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(is_main_cfg) {
|
||||
base = directory_name(file);
|
||||
// HACK: fool file_name() into giving us the parent directory name
|
||||
// alone by making base seem not like a directory path,
|
||||
// otherwise it returns an empty string.
|
||||
strip_trailing_dir_separators(base);
|
||||
base = file_name(base);
|
||||
} else {
|
||||
base = filename;
|
||||
}
|
||||
|
||||
if(base.empty()) {
|
||||
// We did something wrong. In the interest of not messing up the
|
||||
// report, leave the original filename intact.
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
// Display the name as an add-on name instead of a filename.
|
||||
//
|
||||
|
||||
if(!is_main_cfg) {
|
||||
// Remove the file extension first.
|
||||
static const std::string wml_suffix = ".cfg";
|
||||
|
||||
if(base.size() > wml_suffix.size()) {
|
||||
const size_t suffix_pos = base.size() - wml_suffix.size();
|
||||
if(base.substr(suffix_pos) == wml_suffix) {
|
||||
base.erase(suffix_pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(have_addon_install_info(base)) {
|
||||
// _info.cfg may have the add-on's title starting with 1.11.7,
|
||||
// if the add-on was downloaded using the revised _info.cfg writer.
|
||||
config cfg;
|
||||
get_addon_install_info(base, cfg);
|
||||
|
||||
const config& info_cfg = cfg.child("info");
|
||||
|
||||
if(info_cfg) {
|
||||
file = info_cfg["title"].str();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to using a synthetic title with underscores replaced with
|
||||
// whitespace.
|
||||
file = make_addon_title(base);
|
||||
}
|
||||
|
||||
if(files.size() == 1) {
|
||||
return utils::indent(files.front());
|
||||
}
|
||||
|
||||
return utils::bullet_list(files);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace gui2
|
||||
{
|
||||
|
||||
/*WIKI
|
||||
* @page = GUIWindowDefinitionWML
|
||||
* @order = 2_wml_error
|
||||
*
|
||||
* == WML error ==
|
||||
*
|
||||
* Dialog used to report WML parser or preprocessor errors.
|
||||
*
|
||||
* @begin{table}{dialog_widgets}
|
||||
*
|
||||
* summary & & control & m &
|
||||
* Label used for displaying a brief summary of the error(s). $
|
||||
*
|
||||
* files & & control & m &
|
||||
* Label used to display the list of affected add-ons or files, if
|
||||
* applicable. It is hidden otherwise. It is recommended to place it
|
||||
* after the summary label. $
|
||||
*
|
||||
* post_summary & & control & m &
|
||||
* Label used for displaying instructions for reporting the error.
|
||||
* It is recommended to place it after the file list label. It may be
|
||||
* hidden if empty. $
|
||||
*
|
||||
* details & & control & m &
|
||||
* Full report of the parser or preprocessor error(s) found. $
|
||||
*
|
||||
* copy & & button & m &
|
||||
* Button that the user can click on to copy the error report to the
|
||||
* system clipboard. $
|
||||
*
|
||||
* @end{table}
|
||||
*/
|
||||
|
||||
REGISTER_DIALOG(wml_error)
|
||||
|
||||
twml_error::twml_error(const std::string& summary,
|
||||
const std::string& post_summary,
|
||||
const std::vector<std::string>& files,
|
||||
const std::string& details)
|
||||
: have_files_(!files.empty())
|
||||
, have_post_summary_(!post_summary.empty())
|
||||
, report_()
|
||||
{
|
||||
const std::string& file_list_text = format_file_list(files);
|
||||
|
||||
report_ = summary;
|
||||
|
||||
if(!file_list_text.empty()) {
|
||||
report_ += "\n" + file_list_text;
|
||||
}
|
||||
|
||||
if(!post_summary.empty()) {
|
||||
report_ += "\n\n" + post_summary;
|
||||
}
|
||||
|
||||
report_ += "\n\n";
|
||||
report_ += _("Details:");
|
||||
report_ += "\n\n" + utils::indent(details);
|
||||
// Since this is likely to be pasted into a text file, add a final line
|
||||
// break for convenience, since otherwise the report ends mid-line.
|
||||
report_ += "\n";
|
||||
|
||||
register_label("summary", true, summary);
|
||||
register_label("post_summary", true, post_summary);
|
||||
register_label("files", true, file_list_text);
|
||||
register_label("details", true, details);
|
||||
}
|
||||
|
||||
void twml_error::pre_show(CVideo& /*video*/, twindow& window)
|
||||
{
|
||||
if(!have_files_) {
|
||||
tcontrol& filelist = find_widget<tcontrol>(&window, "files", false);
|
||||
filelist.set_visible(tcontrol::tvisible::invisible);
|
||||
}
|
||||
|
||||
if(!have_post_summary_) {
|
||||
tcontrol& post_summary = find_widget<tcontrol>(&window,
|
||||
"post_summary", false);
|
||||
post_summary.set_visible(tcontrol::tvisible::invisible);
|
||||
}
|
||||
|
||||
tbutton& copy_button = find_widget<tbutton>(&window, "copy", false);
|
||||
|
||||
connect_signal_mouse_left_click(
|
||||
copy_button, boost::bind(&twml_error::copy_report_callback, this));
|
||||
}
|
||||
|
||||
void twml_error::copy_report_callback()
|
||||
{
|
||||
copy_to_clipboard(report_, false);
|
||||
}
|
||||
|
||||
} // end namespace gui2
|
74
src/gui/dialogs/wml_error.hpp
Normal file
74
src/gui/dialogs/wml_error.hpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Ignacio R. Morelle <shadowm2006@gmail.com>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#ifndef GUI_DIALOGS_WML_ERROR_HPP_INCLUDED
|
||||
#define GUI_DIALOGS_WML_ERROR_HPP_INCLUDED
|
||||
|
||||
#include "gui/dialogs/dialog.hpp"
|
||||
|
||||
namespace gui2 {
|
||||
|
||||
/** WML preprocessor/parser error report dialog. */
|
||||
class twml_error : public tdialog
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param summary Leading summary line for the report.
|
||||
* @param post_summary Additional line with instructions for the user, may
|
||||
* be empty.
|
||||
* @param files List of WML files on which errors were detected.
|
||||
* @param details Detailed WML preprocessor/parser error report.
|
||||
*/
|
||||
twml_error(const std::string& summary,
|
||||
const std::string& post_summary,
|
||||
const std::vector<std::string>& files,
|
||||
const std::string& details);
|
||||
|
||||
/** The display function; see @ref tdialog for more information. */
|
||||
static void display(const std::string& summary,
|
||||
const std::string& post_summary,
|
||||
const std::vector<std::string>& files,
|
||||
const std::string& details,
|
||||
CVideo& video)
|
||||
{
|
||||
twml_error(summary, post_summary, files, details).show(video);
|
||||
}
|
||||
|
||||
/** The display function; see @ref tdialog for more information. */
|
||||
static void display(const std::string& summary,
|
||||
const std::string& details,
|
||||
CVideo& video)
|
||||
{
|
||||
display(summary, "", std::vector<std::string>(), details, video);
|
||||
}
|
||||
|
||||
private:
|
||||
bool have_files_;
|
||||
bool have_post_summary_;
|
||||
std::string report_; // Plain text report for copying to clipboard.
|
||||
|
||||
/** Inherited from tdialog, implemented by REGISTER_DIALOG. */
|
||||
virtual const std::string& window_id() const;
|
||||
|
||||
/** Inherited from tdialog. */
|
||||
void pre_show(CVideo& video, twindow& window);
|
||||
|
||||
void copy_report_callback();
|
||||
};
|
||||
|
||||
} // end namespace gui2
|
||||
|
||||
#endif
|
|
@ -64,7 +64,7 @@ private:
|
|||
void parse_element();
|
||||
void parse_variable();
|
||||
std::string lineno_string(utils::string_map &map, std::string const &lineno,
|
||||
const char *error_string);
|
||||
const char *error_string, const char *hint_string = NULL);
|
||||
void error(const std::string& message);
|
||||
|
||||
config& cfg_;
|
||||
|
@ -142,7 +142,8 @@ void parser::operator()()
|
|||
std::stringstream ss;
|
||||
ss << elements.top().start_line << " " << elements.top().file;
|
||||
error(lineno_string(i18n_symbols, ss.str(),
|
||||
N_("Missing closing tag for tag [$tag] at $pos")));
|
||||
N_("Missing closing tag for tag [$tag]"),
|
||||
N_("at $pos")));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,7 +206,8 @@ void parser::parse_element()
|
|||
std::stringstream ss;
|
||||
ss << elements.top().start_line << " " << elements.top().file;
|
||||
error(lineno_string(i18n_symbols, ss.str(),
|
||||
N_("Found invalid closing tag [/$tag2] for tag [$tag1] (opened at $pos)")));
|
||||
N_("Found invalid closing tag [/$tag2] for tag [$tag1]"),
|
||||
N_("opened at $pos")));
|
||||
}
|
||||
if(validator_){
|
||||
element & el= elements.top();
|
||||
|
@ -339,10 +341,18 @@ void parser::parse_variable()
|
|||
* This function is crap. Don't use it on a string_map with prefixes.
|
||||
*/
|
||||
std::string parser::lineno_string(utils::string_map &i18n_symbols,
|
||||
std::string const &lineno, const char *error_string)
|
||||
std::string const &lineno,
|
||||
const char *error_string,
|
||||
const char *hint_string)
|
||||
{
|
||||
i18n_symbols["pos"] = ::lineno_string(lineno);
|
||||
std::string result = _(error_string);
|
||||
|
||||
if(hint_string != NULL) {
|
||||
result += "\n ";
|
||||
result += hint_string;
|
||||
}
|
||||
|
||||
BOOST_FOREACH(utils::string_map::value_type& var, i18n_symbols)
|
||||
boost::algorithm::replace_all(result, std::string("$") + var.first, std::string(var.second));
|
||||
return result;
|
||||
|
@ -352,18 +362,18 @@ void parser::error(const std::string& error_type)
|
|||
{
|
||||
utils::string_map i18n_symbols;
|
||||
i18n_symbols["error"] = error_type;
|
||||
i18n_symbols["value"] = tok_->current_token().value;
|
||||
std::stringstream ss;
|
||||
ss << tok_->get_start_line() << " " << tok_->get_file();
|
||||
#ifdef DEBUG
|
||||
i18n_symbols["value"] = tok_->current_token().value;
|
||||
i18n_symbols["previous_value"] = tok_->previous_token().value;
|
||||
throw config::error(
|
||||
lineno_string(i18n_symbols, ss.str(),
|
||||
N_("$error, value '$value', previous '$previous_value' at $pos")));
|
||||
N_("$error"), N_("at $pos (value '$value', previous '$previous_value')")));
|
||||
#else
|
||||
throw config::error(
|
||||
lineno_string(i18n_symbols, ss.str(),
|
||||
N_("$error, value '$value' at $pos")));
|
||||
N_("$error"), N_("at $pos")));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -55,6 +55,8 @@ static t_file_number_map file_number_map;
|
|||
|
||||
static bool encode_filename = true;
|
||||
|
||||
static std::string preprocessor_error_detail_prefix = "\n ";
|
||||
|
||||
// get filename associated to this code
|
||||
static std::string get_filename(const std::string& file_code){
|
||||
if(!encode_filename)
|
||||
|
@ -326,7 +328,8 @@ std::string lineno_string(const std::string &lineno)
|
|||
{
|
||||
std::vector< std::string > pos = utils::quoted_split(lineno, ' ');
|
||||
std::vector< std::string >::const_iterator i = pos.begin(), end = pos.end();
|
||||
std::string included_from = " included from ";
|
||||
std::string included_from =
|
||||
preprocessor_error_detail_prefix + "included from ";
|
||||
std::string res;
|
||||
while (i != end) {
|
||||
std::string const &line = *(i++);
|
||||
|
@ -348,7 +351,7 @@ void preprocessor_streambuf::error(const std::string& error_type, int l)
|
|||
std::ostringstream pos;
|
||||
pos << l << ' ' << location_;
|
||||
position = lineno_string(pos.str());
|
||||
error = error_type + " at " + position;
|
||||
error = error_type + preprocessor_error_detail_prefix + "at " + position;
|
||||
ERR_CF << error << '\n';
|
||||
throw preproc_config::error(error);
|
||||
}
|
||||
|
|
|
@ -741,6 +741,35 @@ bool wildcard_string_match(const std::string& str, const std::string& match) {
|
|||
return matches;
|
||||
}
|
||||
|
||||
std::string indent(const std::string& string, size_t indent_size)
|
||||
{
|
||||
if(string.empty() || indent_size == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const std::string indent(indent_size, ' ');
|
||||
|
||||
const std::vector<std::string>& lines = split(string, '\x0A', 0);
|
||||
std::string res;
|
||||
|
||||
for(size_t lno = 0; lno < lines.size();) {
|
||||
const std::string& line = lines[lno];
|
||||
|
||||
// Lines containing only a carriage return count as empty.
|
||||
if(!line.empty() && line != "\x0D") {
|
||||
res += indent;
|
||||
}
|
||||
|
||||
res += line;
|
||||
|
||||
if(++lno < lines.size()) {
|
||||
res += '\x0A';
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector< std::string > quoted_split(std::string const &val, char c, int flags, char quote)
|
||||
{
|
||||
std::vector<std::string> res;
|
||||
|
|
|
@ -191,6 +191,17 @@ std::string bullet_list(const T& v, size_t indent = 4, const std::string& bullet
|
|||
return str.str();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indent a block of text.
|
||||
*
|
||||
* Only lines with content are changed; empty lines are left intact.
|
||||
*
|
||||
* @param string Text to indent.
|
||||
* @param indent_size Number of indentation units to use.
|
||||
* @param indent_unit Indentation unit.
|
||||
*/
|
||||
std::string indent(const std::string& string, size_t indent_size = 4);
|
||||
|
||||
/**
|
||||
* This function is identical to split(), except it does not split
|
||||
* when it otherwise would if the previous character was identical to the parameter 'quote'.
|
||||
|
|
Loading…
Add table
Reference in a new issue