Remove misc.rs
(#491)
Put everything that was in `misc.rs` into their own files, with some opportunistic refactoring, because why not.
This commit is contained in:
parent
ca4f2a44ed
commit
3de31b3c02
@ -31,9 +31,8 @@ pub(crate) use crate::testing;
|
|||||||
|
|
||||||
// functions
|
// functions
|
||||||
pub(crate) use crate::{
|
pub(crate) use crate::{
|
||||||
load_dotenv::load_dotenv,
|
default::default, empty::empty, load_dotenv::load_dotenv, output::output,
|
||||||
misc::{default, empty},
|
write_message_context::write_message_context,
|
||||||
output::output,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// structs and enums
|
// structs and enums
|
||||||
@ -41,12 +40,13 @@ pub(crate) use crate::{
|
|||||||
alias::Alias, alias_resolver::AliasResolver, assignment_evaluator::AssignmentEvaluator,
|
alias::Alias, alias_resolver::AliasResolver, assignment_evaluator::AssignmentEvaluator,
|
||||||
assignment_resolver::AssignmentResolver, color::Color, compilation_error::CompilationError,
|
assignment_resolver::AssignmentResolver, color::Color, compilation_error::CompilationError,
|
||||||
compilation_error_kind::CompilationErrorKind, config::Config, config_error::ConfigError,
|
compilation_error_kind::CompilationErrorKind, config::Config, config_error::ConfigError,
|
||||||
expression::Expression, fragment::Fragment, function::Function,
|
count::Count, enclosure::Enclosure, expression::Expression, fragment::Fragment,
|
||||||
function_context::FunctionContext, functions::Functions, interrupt_guard::InterruptGuard,
|
function::Function, function_context::FunctionContext, functions::Functions,
|
||||||
interrupt_handler::InterruptHandler, justfile::Justfile, lexer::Lexer, output_error::OutputError,
|
interrupt_guard::InterruptGuard, interrupt_handler::InterruptHandler, justfile::Justfile,
|
||||||
parameter::Parameter, parser::Parser, platform::Platform, position::Position, recipe::Recipe,
|
lexer::Lexer, list::List, output_error::OutputError, parameter::Parameter, parser::Parser,
|
||||||
recipe_context::RecipeContext, recipe_resolver::RecipeResolver, runtime_error::RuntimeError,
|
platform::Platform, position::Position, recipe::Recipe, recipe_context::RecipeContext,
|
||||||
search_error::SearchError, shebang::Shebang, state::State, string_literal::StringLiteral,
|
recipe_resolver::RecipeResolver, runtime_error::RuntimeError, search_error::SearchError,
|
||||||
|
shebang::Shebang, show_whitespace::ShowWhitespace, state::State, string_literal::StringLiteral,
|
||||||
subcommand::Subcommand, token::Token, token_kind::TokenKind, use_color::UseColor,
|
subcommand::Subcommand, token::Token, token_kind::TokenKind, use_color::UseColor,
|
||||||
variables::Variables, verbosity::Verbosity, warning::Warning,
|
variables::Variables, verbosity::Verbosity, warning::Warning,
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
use crate::misc::{maybe_s, show_whitespace, write_message_context, Or};
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub(crate) struct CompilationError<'a> {
|
pub(crate) struct CompilationError<'a> {
|
||||||
pub(crate) text: &'a str,
|
pub(crate) text: &'a str,
|
||||||
@ -82,7 +80,7 @@ impl<'a> Display for CompilationError<'a> {
|
|||||||
ref expected,
|
ref expected,
|
||||||
found,
|
found,
|
||||||
} => {
|
} => {
|
||||||
writeln!(f, "Expected {}, but found {}", Or(expected), found)?;
|
writeln!(f, "Expected {}, but found {}", List::or(expected), found)?;
|
||||||
}
|
}
|
||||||
DuplicateAlias { alias, first } => {
|
DuplicateAlias { alias, first } => {
|
||||||
writeln!(
|
writeln!(
|
||||||
@ -139,7 +137,7 @@ impl<'a> Display for CompilationError<'a> {
|
|||||||
f,
|
f,
|
||||||
"Found a mix of tabs and spaces in leading whitespace: `{}`\n\
|
"Found a mix of tabs and spaces in leading whitespace: `{}`\n\
|
||||||
Leading whitespace may consist of tabs or spaces, but not both",
|
Leading whitespace may consist of tabs or spaces, but not both",
|
||||||
show_whitespace(whitespace)
|
ShowWhitespace(whitespace)
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
ExtraLeadingWhitespace => {
|
ExtraLeadingWhitespace => {
|
||||||
@ -152,10 +150,10 @@ impl<'a> Display for CompilationError<'a> {
|
|||||||
} => {
|
} => {
|
||||||
writeln!(
|
writeln!(
|
||||||
f,
|
f,
|
||||||
"Function `{}` called with {} argument{} but takes {}",
|
"Function `{}` called with {} {} but takes {}",
|
||||||
function,
|
function,
|
||||||
found,
|
found,
|
||||||
maybe_s(found),
|
Count("argument", found),
|
||||||
expected
|
expected
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
@ -164,8 +162,8 @@ impl<'a> Display for CompilationError<'a> {
|
|||||||
f,
|
f,
|
||||||
"Recipe line has inconsistent leading whitespace. \
|
"Recipe line has inconsistent leading whitespace. \
|
||||||
Recipe started with `{}` but found line with `{}`",
|
Recipe started with `{}` but found line with `{}`",
|
||||||
show_whitespace(expected),
|
ShowWhitespace(expected),
|
||||||
show_whitespace(found)
|
ShowWhitespace(found)
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
UnknownAliasTarget { alias, target } => {
|
UnknownAliasTarget { alias, target } => {
|
||||||
|
25
src/count.rs
Normal file
25
src/count.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
use crate::common::*;
|
||||||
|
|
||||||
|
pub struct Count<T: Display>(pub T, pub usize);
|
||||||
|
|
||||||
|
impl<T: Display> Display for Count<T> {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
if self.1 == 1 {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
} else {
|
||||||
|
write!(f, "{}s", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn count() {
|
||||||
|
assert_eq!(Count("dog", 0).to_string(), "dogs");
|
||||||
|
assert_eq!(Count("dog", 1).to_string(), "dog");
|
||||||
|
assert_eq!(Count("dog", 2).to_string(), "dogs");
|
||||||
|
}
|
||||||
|
}
|
3
src/default.rs
Normal file
3
src/default.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub(crate) fn default<T: Default>() -> T {
|
||||||
|
Default::default()
|
||||||
|
}
|
5
src/empty.rs
Normal file
5
src/empty.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
use crate::common::*;
|
||||||
|
|
||||||
|
pub(crate) fn empty<T, C: iter::FromIterator<T>>() -> C {
|
||||||
|
iter::empty().collect()
|
||||||
|
}
|
31
src/enclosure.rs
Normal file
31
src/enclosure.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use crate::common::*;
|
||||||
|
|
||||||
|
pub struct Enclosure<T: Display> {
|
||||||
|
enclosure: &'static str,
|
||||||
|
value: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Display> Enclosure<T> {
|
||||||
|
pub fn tick(value: T) -> Enclosure<T> {
|
||||||
|
Enclosure {
|
||||||
|
enclosure: "`",
|
||||||
|
value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Display> Display for Enclosure<T> {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}{}{}", self.enclosure, self.value, self.enclosure)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tick() {
|
||||||
|
assert_eq!(Enclosure::tick("foo").to_string(), "`foo`")
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,10 @@ mod compilation_error;
|
|||||||
mod compilation_error_kind;
|
mod compilation_error_kind;
|
||||||
mod config;
|
mod config;
|
||||||
mod config_error;
|
mod config_error;
|
||||||
|
mod count;
|
||||||
|
mod default;
|
||||||
|
mod empty;
|
||||||
|
mod enclosure;
|
||||||
mod expression;
|
mod expression;
|
||||||
mod fragment;
|
mod fragment;
|
||||||
mod function;
|
mod function;
|
||||||
@ -31,8 +35,8 @@ mod interrupt_guard;
|
|||||||
mod interrupt_handler;
|
mod interrupt_handler;
|
||||||
mod justfile;
|
mod justfile;
|
||||||
mod lexer;
|
mod lexer;
|
||||||
|
mod list;
|
||||||
mod load_dotenv;
|
mod load_dotenv;
|
||||||
mod misc;
|
|
||||||
mod ordinal;
|
mod ordinal;
|
||||||
mod output;
|
mod output;
|
||||||
mod output_error;
|
mod output_error;
|
||||||
@ -50,6 +54,7 @@ mod runtime_error;
|
|||||||
mod search;
|
mod search;
|
||||||
mod search_error;
|
mod search_error;
|
||||||
mod shebang;
|
mod shebang;
|
||||||
|
mod show_whitespace;
|
||||||
mod state;
|
mod state;
|
||||||
mod string_literal;
|
mod string_literal;
|
||||||
mod subcommand;
|
mod subcommand;
|
||||||
@ -59,6 +64,7 @@ mod use_color;
|
|||||||
mod variables;
|
mod variables;
|
||||||
mod verbosity;
|
mod verbosity;
|
||||||
mod warning;
|
mod warning;
|
||||||
|
mod write_message_context;
|
||||||
|
|
||||||
pub use crate::run::run;
|
pub use crate::run::run;
|
||||||
|
|
||||||
|
123
src/list.rs
Normal file
123
src/list.rs
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
use crate::common::*;
|
||||||
|
|
||||||
|
pub struct List<T: Display, I: Iterator<Item = T> + Clone> {
|
||||||
|
conjunction: &'static str,
|
||||||
|
values: I,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Display, I: Iterator<Item = T> + Clone> List<T, I> {
|
||||||
|
pub fn or<II: IntoIterator<Item = T, IntoIter = I>>(values: II) -> List<T, I> {
|
||||||
|
List {
|
||||||
|
conjunction: "or",
|
||||||
|
values: values.into_iter(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn and<II: IntoIterator<Item = T, IntoIter = I>>(values: II) -> List<T, I> {
|
||||||
|
List {
|
||||||
|
conjunction: "and",
|
||||||
|
values: values.into_iter(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn or_ticked<II: IntoIterator<Item = T, IntoIter = I>>(
|
||||||
|
values: II,
|
||||||
|
) -> List<Enclosure<T>, impl Iterator<Item = Enclosure<T>> + Clone> {
|
||||||
|
List::or(values.into_iter().map(Enclosure::tick))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn and_ticked<II: IntoIterator<Item = T, IntoIter = I>>(
|
||||||
|
values: II,
|
||||||
|
) -> List<Enclosure<T>, impl Iterator<Item = Enclosure<T>> + Clone> {
|
||||||
|
List::and(values.into_iter().map(Enclosure::tick))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Display, I: Iterator<Item = T> + Clone> Display for List<T, I> {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
let mut values = self.values.clone().fuse();
|
||||||
|
|
||||||
|
if let Some(first) = values.next() {
|
||||||
|
write!(f, "{}", first)?;
|
||||||
|
} else {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let second = values.next();
|
||||||
|
|
||||||
|
if second.is_none() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let third = values.next();
|
||||||
|
|
||||||
|
if let (Some(second), None) = (second.as_ref(), third.as_ref()) {
|
||||||
|
write!(f, " {} {}", self.conjunction, second)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut current = second;
|
||||||
|
let mut next = third;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match (current, next) {
|
||||||
|
(Some(c), Some(n)) => {
|
||||||
|
write!(f, ", {}", c)?;
|
||||||
|
current = Some(n);
|
||||||
|
next = values.next();
|
||||||
|
}
|
||||||
|
(Some(c), None) => {
|
||||||
|
write!(f, ", {} {}", self.conjunction, c)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
_ => panic!("Iterator was fused, but returned Some after None"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn or() {
|
||||||
|
assert_eq!("1", List::or(&[1]).to_string());
|
||||||
|
assert_eq!("1 or 2", List::or(&[1, 2]).to_string());
|
||||||
|
assert_eq!("1, 2, or 3", List::or(&[1, 2, 3]).to_string());
|
||||||
|
assert_eq!("1, 2, 3, or 4", List::or(&[1, 2, 3, 4]).to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn and() {
|
||||||
|
assert_eq!("1", List::and(&[1]).to_string());
|
||||||
|
assert_eq!("1 and 2", List::and(&[1, 2]).to_string());
|
||||||
|
assert_eq!("1, 2, and 3", List::and(&[1, 2, 3]).to_string());
|
||||||
|
assert_eq!("1, 2, 3, and 4", List::and(&[1, 2, 3, 4]).to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn or_ticked() {
|
||||||
|
assert_eq!("`1`", List::or_ticked(&[1]).to_string());
|
||||||
|
assert_eq!("`1` or `2`", List::or_ticked(&[1, 2]).to_string());
|
||||||
|
assert_eq!("`1`, `2`, or `3`", List::or_ticked(&[1, 2, 3]).to_string());
|
||||||
|
assert_eq!(
|
||||||
|
"`1`, `2`, `3`, or `4`",
|
||||||
|
List::or_ticked(&[1, 2, 3, 4]).to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn and_ticked() {
|
||||||
|
assert_eq!("`1`", List::and_ticked(&[1]).to_string());
|
||||||
|
assert_eq!("`1` and `2`", List::and_ticked(&[1, 2]).to_string());
|
||||||
|
assert_eq!(
|
||||||
|
"`1`, `2`, and `3`",
|
||||||
|
List::and_ticked(&[1, 2, 3]).to_string()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
"`1`, `2`, `3`, and `4`",
|
||||||
|
List::and_ticked(&[1, 2, 3, 4]).to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
167
src/misc.rs
167
src/misc.rs
@ -1,167 +0,0 @@
|
|||||||
use crate::common::*;
|
|
||||||
|
|
||||||
pub(crate) fn show_whitespace(text: &str) -> String {
|
|
||||||
text
|
|
||||||
.chars()
|
|
||||||
.map(|c| match c {
|
|
||||||
'\t' => '␉',
|
|
||||||
' ' => '␠',
|
|
||||||
_ => c,
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn default<T: Default>() -> T {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn empty<T, C: iter::FromIterator<T>>() -> C {
|
|
||||||
iter::empty().collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn ticks<T: Display>(ts: &[T]) -> Vec<Tick<T>> {
|
|
||||||
ts.iter().map(Tick).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn maybe_s(n: usize) -> &'static str {
|
|
||||||
if n == 1 {
|
|
||||||
""
|
|
||||||
} else {
|
|
||||||
"s"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn conjoin<T: Display>(
|
|
||||||
f: &mut Formatter,
|
|
||||||
values: &[T],
|
|
||||||
conjunction: &str,
|
|
||||||
) -> Result<(), fmt::Error> {
|
|
||||||
match values.len() {
|
|
||||||
0 => {}
|
|
||||||
1 => write!(f, "{}", values[0])?,
|
|
||||||
2 => write!(f, "{} {} {}", values[0], conjunction, values[1])?,
|
|
||||||
_ => {
|
|
||||||
for (i, item) in values.iter().enumerate() {
|
|
||||||
write!(f, "{}", item)?;
|
|
||||||
if i == values.len() - 1 {
|
|
||||||
} else if i == values.len() - 2 {
|
|
||||||
write!(f, ", {} ", conjunction)?;
|
|
||||||
} else {
|
|
||||||
write!(f, ", ")?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn write_message_context(
|
|
||||||
f: &mut Formatter,
|
|
||||||
color: Color,
|
|
||||||
text: &str,
|
|
||||||
offset: usize,
|
|
||||||
line: usize,
|
|
||||||
column: usize,
|
|
||||||
width: usize,
|
|
||||||
) -> Result<(), fmt::Error> {
|
|
||||||
let width = if width == 0 { 1 } else { width };
|
|
||||||
|
|
||||||
let line_number = line.ordinal();
|
|
||||||
match text.lines().nth(line) {
|
|
||||||
Some(line) => {
|
|
||||||
let mut i = 0;
|
|
||||||
let mut space_column = 0;
|
|
||||||
let mut space_line = String::new();
|
|
||||||
let mut space_width = 0;
|
|
||||||
for c in line.chars() {
|
|
||||||
if c == '\t' {
|
|
||||||
space_line.push_str(" ");
|
|
||||||
if i < column {
|
|
||||||
space_column += 4;
|
|
||||||
}
|
|
||||||
if i >= column && i < column + width {
|
|
||||||
space_width += 4;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if i < column {
|
|
||||||
space_column += UnicodeWidthChar::width(c).unwrap_or(0);
|
|
||||||
}
|
|
||||||
if i >= column && i < column + width {
|
|
||||||
space_width += UnicodeWidthChar::width(c).unwrap_or(0);
|
|
||||||
}
|
|
||||||
space_line.push(c);
|
|
||||||
}
|
|
||||||
i += c.len_utf8();
|
|
||||||
}
|
|
||||||
let line_number_width = line_number.to_string().len();
|
|
||||||
writeln!(f, "{0:1$} |", "", line_number_width)?;
|
|
||||||
writeln!(f, "{} | {}", line_number, space_line)?;
|
|
||||||
write!(f, "{0:1$} |", "", line_number_width)?;
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
" {0:1$}{2}{3:^<4$}{5}",
|
|
||||||
"",
|
|
||||||
space_column,
|
|
||||||
color.prefix(),
|
|
||||||
"",
|
|
||||||
space_width,
|
|
||||||
color.suffix()
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
if offset != text.len() {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"internal error: Error has invalid line number: {}",
|
|
||||||
line_number
|
|
||||||
)?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct And<'a, T: 'a + Display>(pub(crate) &'a [T]);
|
|
||||||
|
|
||||||
impl<'a, T: Display> Display for And<'a, T> {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
|
|
||||||
conjoin(f, self.0, "and")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct Or<'a, T: 'a + Display>(pub(crate) &'a [T]);
|
|
||||||
|
|
||||||
impl<'a, T: Display> Display for Or<'a, T> {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
|
|
||||||
conjoin(f, self.0, "or")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct Tick<'a, T: 'a + Display>(pub(crate) &'a T);
|
|
||||||
|
|
||||||
impl<'a, T: Display> Display for Tick<'a, T> {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
|
|
||||||
write!(f, "`{}`", self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn conjoin_or() {
|
|
||||||
assert_eq!("1", Or(&[1]).to_string());
|
|
||||||
assert_eq!("1 or 2", Or(&[1, 2]).to_string());
|
|
||||||
assert_eq!("1, 2, or 3", Or(&[1, 2, 3]).to_string());
|
|
||||||
assert_eq!("1, 2, 3, or 4", Or(&[1, 2, 3, 4]).to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn conjoin_and() {
|
|
||||||
assert_eq!("1", And(&[1]).to_string());
|
|
||||||
assert_eq!("1 and 2", And(&[1, 2]).to_string());
|
|
||||||
assert_eq!("1, 2, and 3", And(&[1, 2, 3]).to_string());
|
|
||||||
assert_eq!("1, 2, 3, and 4", And(&[1, 2, 3, 4]).to_string());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
use crate::{interrupt_handler::InterruptHandler, misc::maybe_s};
|
use crate::interrupt_handler::InterruptHandler;
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
fn edit<P: AsRef<OsStr>>(path: P) -> Result<(), i32> {
|
fn edit<P: AsRef<OsStr>>(path: P) -> Result<(), i32> {
|
||||||
@ -285,10 +285,10 @@ pub fn run() -> Result<(), i32> {
|
|||||||
let min_arguments = recipe.min_arguments();
|
let min_arguments = recipe.min_arguments();
|
||||||
if min_arguments > 0 {
|
if min_arguments > 0 {
|
||||||
die!(
|
die!(
|
||||||
"Recipe `{}` cannot be used as default recipe since it requires at least {} argument{}.",
|
"Recipe `{}` cannot be used as default recipe since it requires at least {} {}.",
|
||||||
recipe.name,
|
recipe.name,
|
||||||
min_arguments,
|
min_arguments,
|
||||||
maybe_s(min_arguments)
|
Count("argument", min_arguments),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
vec![recipe.name]
|
vec![recipe.name]
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
use crate::misc::{maybe_s, ticks, write_message_context, And, Or, Tick};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum RuntimeError<'a> {
|
pub(crate) enum RuntimeError<'a> {
|
||||||
ArgumentCountMismatch {
|
ArgumentCountMismatch {
|
||||||
@ -102,9 +100,9 @@ impl<'a> Display for RuntimeError<'a> {
|
|||||||
} => {
|
} => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"Justfile does not contain recipe{} {}.",
|
"Justfile does not contain {} {}.",
|
||||||
maybe_s(recipes.len()),
|
Count("recipe", recipes.len()),
|
||||||
Or(&ticks(recipes))
|
List::or_ticked(recipes),
|
||||||
)?;
|
)?;
|
||||||
if let Some(suggestion) = *suggestion {
|
if let Some(suggestion) = *suggestion {
|
||||||
write!(f, "\nDid you mean `{}`?", suggestion)?;
|
write!(f, "\nDid you mean `{}`?", suggestion)?;
|
||||||
@ -113,9 +111,9 @@ impl<'a> Display for RuntimeError<'a> {
|
|||||||
UnknownOverrides { ref overrides } => {
|
UnknownOverrides { ref overrides } => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"Variable{} {} overridden on the command line but not present in justfile",
|
"{} {} overridden on the command line but not present in justfile",
|
||||||
maybe_s(overrides.len()),
|
Count("Variable", overrides.len()),
|
||||||
And(&overrides.iter().map(Tick).collect::<Vec<_>>())
|
List::and_ticked(overrides),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
ArgumentCountMismatch {
|
ArgumentCountMismatch {
|
||||||
@ -129,29 +127,29 @@ impl<'a> Display for RuntimeError<'a> {
|
|||||||
let expected = min;
|
let expected = min;
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"Recipe `{}` got {} argument{} but {}takes {}",
|
"Recipe `{}` got {} {} but {}takes {}",
|
||||||
recipe,
|
recipe,
|
||||||
found,
|
found,
|
||||||
maybe_s(found),
|
Count("argument", found),
|
||||||
if expected < found { "only " } else { "" },
|
if expected < found { "only " } else { "" },
|
||||||
expected
|
expected
|
||||||
)?;
|
)?;
|
||||||
} else if found < min {
|
} else if found < min {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"Recipe `{}` got {} argument{} but takes at least {}",
|
"Recipe `{}` got {} {} but takes at least {}",
|
||||||
recipe,
|
recipe,
|
||||||
found,
|
found,
|
||||||
maybe_s(found),
|
Count("argument", found),
|
||||||
min
|
min
|
||||||
)?;
|
)?;
|
||||||
} else if found > max {
|
} else if found > max {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"Recipe `{}` got {} argument{} but takes at most {}",
|
"Recipe `{}` got {} {} but takes at most {}",
|
||||||
recipe,
|
recipe,
|
||||||
found,
|
found,
|
||||||
maybe_s(found),
|
Count("argument", found),
|
||||||
max
|
max
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
use std::{fmt, io, path::PathBuf};
|
use crate::common::*;
|
||||||
|
|
||||||
use crate::misc::And;
|
|
||||||
|
|
||||||
pub(crate) enum SearchError {
|
pub(crate) enum SearchError {
|
||||||
MultipleCandidates {
|
MultipleCandidates {
|
||||||
@ -29,11 +27,10 @@ impl fmt::Display for SearchError {
|
|||||||
f,
|
f,
|
||||||
"Multiple candidate justfiles found in `{}`: {}",
|
"Multiple candidate justfiles found in `{}`: {}",
|
||||||
candidates[0].parent().unwrap().display(),
|
candidates[0].parent().unwrap().display(),
|
||||||
And(
|
List::and_ticked(
|
||||||
&candidates
|
candidates
|
||||||
.iter()
|
.iter()
|
||||||
.map(|candidate| format!("`{}`", candidate.file_name().unwrap().to_string_lossy()))
|
.map(|candidate| candidate.file_name().unwrap().to_string_lossy())
|
||||||
.collect::<Vec<String>>()
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SearchError::NotFound => write!(f, "No justfile found"),
|
SearchError::NotFound => write!(f, "No justfile found"),
|
||||||
|
18
src/show_whitespace.rs
Normal file
18
src/show_whitespace.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
use crate::common::*;
|
||||||
|
|
||||||
|
/// String wrapper that uses nonblank characters to display spaces and tabs
|
||||||
|
pub struct ShowWhitespace<'str>(pub &'str str);
|
||||||
|
|
||||||
|
impl<'str> Display for ShowWhitespace<'str> {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
for c in self.0.chars() {
|
||||||
|
match c {
|
||||||
|
'\t' => write!(f, "␉")?,
|
||||||
|
' ' => write!(f, "␠")?,
|
||||||
|
_ => write!(f, "{}", c)?,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
use crate::misc::write_message_context;
|
|
||||||
|
|
||||||
use Warning::*;
|
use Warning::*;
|
||||||
|
|
||||||
|
67
src/write_message_context.rs
Normal file
67
src/write_message_context.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
use crate::common::*;
|
||||||
|
|
||||||
|
pub(crate) fn write_message_context(
|
||||||
|
f: &mut Formatter,
|
||||||
|
color: Color,
|
||||||
|
text: &str,
|
||||||
|
offset: usize,
|
||||||
|
line: usize,
|
||||||
|
column: usize,
|
||||||
|
width: usize,
|
||||||
|
) -> Result<(), fmt::Error> {
|
||||||
|
let width = if width == 0 { 1 } else { width };
|
||||||
|
|
||||||
|
let line_number = line.ordinal();
|
||||||
|
match text.lines().nth(line) {
|
||||||
|
Some(line) => {
|
||||||
|
let mut i = 0;
|
||||||
|
let mut space_column = 0;
|
||||||
|
let mut space_line = String::new();
|
||||||
|
let mut space_width = 0;
|
||||||
|
for c in line.chars() {
|
||||||
|
if c == '\t' {
|
||||||
|
space_line.push_str(" ");
|
||||||
|
if i < column {
|
||||||
|
space_column += 4;
|
||||||
|
}
|
||||||
|
if i >= column && i < column + width {
|
||||||
|
space_width += 4;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if i < column {
|
||||||
|
space_column += UnicodeWidthChar::width(c).unwrap_or(0);
|
||||||
|
}
|
||||||
|
if i >= column && i < column + width {
|
||||||
|
space_width += UnicodeWidthChar::width(c).unwrap_or(0);
|
||||||
|
}
|
||||||
|
space_line.push(c);
|
||||||
|
}
|
||||||
|
i += c.len_utf8();
|
||||||
|
}
|
||||||
|
let line_number_width = line_number.to_string().len();
|
||||||
|
writeln!(f, "{0:1$} |", "", line_number_width)?;
|
||||||
|
writeln!(f, "{} | {}", line_number, space_line)?;
|
||||||
|
write!(f, "{0:1$} |", "", line_number_width)?;
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
" {0:1$}{2}{3:^<4$}{5}",
|
||||||
|
"",
|
||||||
|
space_column,
|
||||||
|
color.prefix(),
|
||||||
|
"",
|
||||||
|
space_width,
|
||||||
|
color.suffix()
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
if offset != text.len() {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"internal error: Error has invalid line number: {}",
|
||||||
|
line_number
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user