# See the License for the specific language governing permissions and
# limitations under the License.
-# Common command-line tool infractructure than handle options and error messages
+# Common command-line tool infrastructure than handle options and error messages
module toolcontext
import opts
import version
import template
+# A warning or an error
class Message
super Comparable
redef type OTHER: Message
+ # The origin of the message in the source code, if any.
var location: nullable Location
+
+ # The category of the message.
+ #
+ # Used by quality-control tool for statistics or to enable/disable things individually.
+ var tag: nullable String
+
+ # The human-readable description of the message.
+ #
+ # It should be short and fit on a single line.
+ # It should also have meaningful information first in case
+ # on truncation by an IDE for instance.
var text: String
# Comparisons are made on message locations.
end
end
+ # A colored version of the message including the original source line
fun to_color_string: String
do
var esc = 27.ascii
- var red = "{esc}[0;31m"
- var bred = "{esc}[1;31m"
- var green = "{esc}[0;32m"
+ #var red = "{esc}[0;31m"
+ #var bred = "{esc}[1;31m"
+ #var green = "{esc}[0;32m"
var yellow = "{esc}[0;33m"
var def = "{esc}[0m"
+ var tag = tag
+ if tag != null then
+ tag = " ({tag})"
+ else
+ tag = ""
+ end
var l = location
if l == null then
- return text
+ return "{text}{tag}"
else if l.file == null then
- return "{yellow}{l}{def}: {text}"
+ return "{yellow}{l}{def}: {text}{tag}"
else
- return "{yellow}{l}{def}: {text}\n{l.colored_line("1;31")}"
+ return "{yellow}{l}{def}: {text}{tag}\n{l.colored_line("1;31")}"
end
end
end
var log_directory: String = "logs"
# Messages
- private var messages: Array[Message] = new Array[Message]
- private var message_sorter: ComparableSorter[Message] = new ComparableSorter[Message]
+ private var messages = new Array[Message]
+ private var message_sorter: Comparator = default_comparator
+ # Output all current stacked messages.
+ # If some errors occurred, exits the program.
fun check_errors
do
if messages.length > 0 then
messages.clear
end
- if error_count > 0 then exit(1)
+ if error_count > 0 then
+ errors_info
+ exit(1)
+ end
+ end
+
+ # Display total error informations
+ fun errors_info
+ do
+ if error_count == 0 and warning_count == 0 then return
+ if opt_no_color.value then return
+ sys.stderr.write "Errors: {error_count}. Warnings: {warning_count}.\n"
end
# Display an error
fun error(l: nullable Location, s: String)
do
- messages.add(new Message(l,s))
+ messages.add(new Message(l,null,s))
error_count = error_count + 1
if opt_stop_on_first_error.value then check_errors
end
check_errors
end
- # Display a warning
- fun warning(l: nullable Location, s: String)
+ # Display a first-level warning.
+ #
+ # First-level warnings are warnings that SHOULD be corrected,
+ # and COULD usually be immediately corrected.
+ #
+ # * There is a simple correction
+ # * There is no reason to let the code this way (no reasonable @supresswarning-like annotation)
+ # * They always are real issues (no false positive)
+ #
+ # First-level warnings are displayed by default (except if option `-q` is given).
+ fun warning(l: nullable Location, tag: String, text: String)
+ do
+ if opt_warning.value.has("no-{tag}") then return
+ if not opt_warning.value.has(tag) and opt_warn.value == 0 then return
+ messages.add(new Message(l, tag, text))
+ warning_count = warning_count + 1
+ if opt_stop_on_first_error.value then check_errors
+ end
+
+ # Display a second-level warning.
+ #
+ # Second-level warnings are warnings that should require investigation,
+ # but cannot always be immediately corrected.
+ #
+ # * The correction could be complex. e.g. require a refactorisation or an API change.
+ # * The correction cannot be done. e.g. Code that use a deprecated API for some compatibility reason.
+ # * There is not a real issue (false positive). Note that this should be unlikely.
+ # * Transitional: While a real warning, it fires a lot in current code, so a transition is needed
+ # in order to fix them before promoting the advice to a warning.
+ #
+ # In order to prevent warning inflation à la Java, second-level warnings are not displayed by
+ # default and require an additional option `-W`.
+ fun advice(l: nullable Location, tag: String, text: String)
do
- if opt_warn.value == 0 then return
- messages.add(new Message(l,s))
+ if opt_warning.value.has("no-{tag}") then return
+ if not opt_warning.value.has(tag) and opt_warn.value <= 1 then return
+ messages.add(new Message(l, tag, text))
warning_count = warning_count + 1
if opt_stop_on_first_error.value then check_errors
end
end
# Global OptionContext
- var option_context: OptionContext = new OptionContext
+ var option_context = new OptionContext
# Option --warn
- var opt_warn: OptionCount = new OptionCount("Show warnings", "-W", "--warn")
+ var opt_warn = new OptionCount("Show more warnings", "-W", "--warn")
+
+ # Option --warning
+ var opt_warning = new OptionArray("Show/hide a specific warning", "-w", "--warning")
# Option --quiet
- var opt_quiet: OptionBool = new OptionBool("Do not show warnings", "-q", "--quiet")
+ var opt_quiet = new OptionBool("Do not show warnings", "-q", "--quiet")
# Option --log
- var opt_log: OptionBool = new OptionBool("Generate various log files", "--log")
+ var opt_log = new OptionBool("Generate various log files", "--log")
# Option --log-dir
- var opt_log_dir: OptionString = new OptionString("Directory where to generate log files", "--log-dir")
+ var opt_log_dir = new OptionString("Directory where to generate log files", "--log-dir")
# Option --help
- var opt_help: OptionBool = new OptionBool("Show Help (This screen)", "-h", "-?", "--help")
+ var opt_help = new OptionBool("Show Help (This screen)", "-h", "-?", "--help")
# Option --version
- var opt_version: OptionBool = new OptionBool("Show version and exit", "--version")
+ var opt_version = new OptionBool("Show version and exit", "--version")
# Option --set-dummy-tool
- var opt_set_dummy_tool: OptionBool = new OptionBool("Set toolname and version to DUMMY. Useful for testing", "--set-dummy-tool")
+ var opt_set_dummy_tool = new OptionBool("Set toolname and version to DUMMY. Useful for testing", "--set-dummy-tool")
# Option --verbose
- var opt_verbose: OptionCount = new OptionCount("Verbose", "-v", "--verbose")
+ var opt_verbose = new OptionCount("Verbose", "-v", "--verbose")
# Option --stop-on-first-error
- var opt_stop_on_first_error: OptionBool = new OptionBool("Stop on first error", "--stop-on-first-error")
+ var opt_stop_on_first_error = new OptionBool("Stop on first error", "--stop-on-first-error")
# Option --no-color
- var opt_no_color: OptionBool = new OptionBool("Do not use color to display errors and warnings", "--no-color")
+ var opt_no_color = new OptionBool("Do not use color to display errors and warnings", "--no-color")
# Option --bash-completion
- var opt_bash_completion: OptionBool = new OptionBool("Generate bash_completion file for this program", "--bash-completion")
+ var opt_bash_completion = new OptionBool("Generate bash_completion file for this program", "--bash-completion")
# Verbose level
var verbose_level: Int = 0
- # Bash completion behavior in command line
- # see `BashCompletion`
- var bash_completion: BashCompletion
-
init
do
- bash_completion = new BashCompletion(self)
- option_context.add_option(opt_warn, opt_quiet, opt_stop_on_first_error, opt_no_color, opt_log, opt_log_dir, opt_help, opt_version, opt_set_dummy_tool, opt_verbose, opt_bash_completion)
+ option_context.add_option(opt_warn, opt_warning, opt_quiet, opt_stop_on_first_error, opt_no_color, opt_log, opt_log_dir, opt_help, opt_version, opt_set_dummy_tool, opt_verbose, opt_bash_completion)
end
# Name, usage and synopsis of the tool.
# A multi-line string is recommmended.
#
# eg. `"Usage: tool [OPTION]... [FILE]...\nDo some things."`
- var tooldescription: String writable = "Usage: [OPTION]... [ARG]..."
+ var tooldescription: String = "Usage: [OPTION]... [ARG]..." is writable
# Does `process_options` should accept an empty sequence of arguments.
# ie. nothing except options.
# Is `false` by default.
#
# If required, if should be set by the client before calling `process_options`
- var accept_no_arguments writable = false
+ var accept_no_arguments = false is writable
# print the full usage of the tool.
# Is called by `process_option` on `--help`.
end
if opt_bash_completion.value then
- print bash_completion.write_to_string
- bash_completion.write_to_file("{sys.program_name}.bash")
+ var bash_completion = new BashCompletion(self)
+ bash_completion.write_to(sys.stdout)
exit 0
end
if opt_set_dummy_tool.value then
return "DUMMY_TOOL"
end
- return sys.program_name
+ return sys.program_name.basename("")
end
# The identified root directory of the Nit project
- var nit_dir: nullable String
+ var nit_dir: nullable String = null
private fun compute_nit_dir: nullable String
do
# --help --only-metamodel --source
# --ignore-visibility --only-parse --stop-on-first-error
#
-# Generated file must be placed in system bash_completion directory `/etc/bash_completion.d/`
-# or in the user directory `~/.bash_completion`.
+# Generated file can be placed in system bash_completion directory `/etc/bash_completion.d/`
+# or source it in `~/.bash_completion`.
class BashCompletion
super Template
addn " return 0"
addn " fi"
end
- addn " _filedir"
- addn "\}"
- addn "complete -F _{name} {name}"
+ addn "\} &&"
+ addn "complete -o default -F _{name} {name}"
end
end