Skip to content

Commit e229643

Browse files
committed
Unify code in emmylua_check and emmylua_doc_cli
Both CLI tools have similar, yet slightly different code and usage. This PR makes them more consistent. In `emmylua_doc_cli`: - added positional parameter `[WORKSPACE]...`; - deprecated `-i, --input` in favor of `[WORKSPACE]...`; - added `-c, --config` to specify location of `.emmyrc.json`/`.luarc.json`; - added `--ignore` to specify ignored files. No short option for `--ignore` because that would clash with `-i, --input`; - deprecated long option `--format` in favor of `--output-format`; - supported `--output stdout` for JSON output; - re-implemented debug output using `log` crate, added `--verbose` option; - contents of `init.rs` are now identical to those from `emmylua_check`. In `emmylua_check`: - added short option `-f` for output format, - supported specifying multiple workspaces.
1 parent 609878e commit e229643

File tree

18 files changed

+376
-205
lines changed

18 files changed

+376
-205
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/emmylua_check/src/cmd_args.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ pub struct CmdArgs {
1414
pub config: Option<Vec<PathBuf>>,
1515

1616
/// Path to the workspace directory
17-
pub workspace: PathBuf,
17+
#[arg(num_args = 1..)]
18+
pub workspace: Vec<PathBuf>,
1819

1920
/// Comma separated list of ignore patterns.
2021
/// Patterns must follow glob syntax
@@ -24,11 +25,17 @@ pub struct CmdArgs {
2425
/// Specify output format
2526
#[cfg_attr(
2627
feature = "cli",
27-
arg(long, default_value = "text", value_enum, ignore_case = true)
28+
arg(
29+
long,
30+
short = 'f',
31+
default_value = "text",
32+
value_enum,
33+
ignore_case = true
34+
)
2835
)]
2936
pub output_format: OutputFormat,
3037

31-
/// Specify output destination (stdout or a file path, only used when output_format is json).
38+
/// Specify output destination (stdout or a file path, only used when output_format is json)
3239
#[cfg_attr(feature = "cli", arg(long, default_value = "stdout"))]
3340
pub output: OutputDestination,
3441

crates/emmylua_check/src/init.rs

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
use std::{path::PathBuf, str::FromStr, sync::Arc};
2-
31
use emmylua_code_analysis::{
42
load_configs, load_workspace_files, update_code_style, DbIndex, EmmyLuaAnalysis, Emmyrc,
53
FileId, LuaFileInfo,
64
};
5+
use fern::Dispatch;
6+
use log::LevelFilter;
7+
use std::{path::PathBuf, str::FromStr, sync::Arc};
78

89
fn root_from_configs(config_paths: &Vec<PathBuf>, fallback: &PathBuf) -> PathBuf {
910
if config_paths.len() != 1 {
@@ -26,13 +27,44 @@ fn root_from_configs(config_paths: &Vec<PathBuf>, fallback: &PathBuf) -> PathBuf
2627
}
2728
}
2829

30+
pub fn setup_logger(verbose: bool) {
31+
let logger = Dispatch::new()
32+
.format(move |out, message, record| {
33+
let (color, reset) = match record.level() {
34+
log::Level::Error => ("\x1b[31m", "\x1b[0m"), // Red
35+
log::Level::Warn => ("\x1b[33m", "\x1b[0m"), // Yellow
36+
log::Level::Info | log::Level::Debug | log::Level::Trace => ("", ""),
37+
};
38+
out.finish(format_args!(
39+
"{}{}: {}{}",
40+
color,
41+
record.level(),
42+
if verbose {
43+
format!("({}) {}", record.target(), message)
44+
} else {
45+
message.to_string()
46+
},
47+
reset
48+
))
49+
})
50+
.level(if verbose {
51+
LevelFilter::Info
52+
} else {
53+
LevelFilter::Warn
54+
})
55+
.chain(std::io::stderr());
56+
57+
if let Err(e) = logger.apply() {
58+
eprintln!("Failed to apply logger: {:?}", e);
59+
}
60+
}
61+
2962
pub fn load_workspace(
30-
workspace_folder: PathBuf,
63+
main_path: PathBuf,
64+
mut workspace_folders: Vec<PathBuf>,
3165
config_paths: Option<Vec<PathBuf>>,
3266
ignore: Option<Vec<String>>,
3367
) -> Option<EmmyLuaAnalysis> {
34-
let mut workspace_folders = vec![workspace_folder];
35-
let main_path = workspace_folders.first()?.clone();
3668
let (config_files, config_root): (Vec<PathBuf>, PathBuf) =
3769
if let Some(config_paths) = config_paths {
3870
(
@@ -176,11 +208,11 @@ pub fn calculate_include_and_exclude(
176208
(include, exclude, exclude_dirs)
177209
}
178210

179-
pub fn get_need_check_ids(db: &DbIndex, files: Vec<FileId>, workspace: &PathBuf) -> Vec<FileId> {
211+
pub fn get_need_check_ids(db: &DbIndex, files: Vec<FileId>, workspace: &[PathBuf]) -> Vec<FileId> {
180212
let mut need_check_files = Vec::new();
181213
for file_id in files {
182214
let file_path = db.get_vfs().get_file_path(&file_id).unwrap();
183-
if file_path.starts_with(workspace) {
215+
if workspace.iter().any(|ws| file_path.starts_with(ws)) {
184216
need_check_files.push(file_id);
185217
}
186218
}

crates/emmylua_check/src/lib.rs

Lines changed: 25 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,52 +4,38 @@ mod output;
44
mod terminal_display;
55

66
pub use cmd_args::*;
7-
use fern::Dispatch;
8-
use log::LevelFilter;
97
use output::output_result;
108
use std::{error::Error, sync::Arc};
119
use tokio_util::sync::CancellationToken;
1210

13-
use crate::init::get_need_check_ids;
11+
use crate::init::{get_need_check_ids, setup_logger};
1412

1513
pub async fn run_check(cmd_args: CmdArgs) -> Result<(), Box<dyn Error + Sync + Send>> {
16-
let mut workspace = cmd_args.workspace;
17-
if !workspace.is_absolute() {
18-
workspace = std::env::current_dir()?.join(workspace);
19-
}
14+
setup_logger(cmd_args.verbose);
2015

21-
let verbose = cmd_args.verbose;
22-
let logger = Dispatch::new()
23-
.format(move |out, message, record| {
24-
let (color, reset) = match record.level() {
25-
log::Level::Error => ("\x1b[31m", "\x1b[0m"), // Red
26-
log::Level::Warn => ("\x1b[33m", "\x1b[0m"), // Yellow
27-
log::Level::Info | log::Level::Debug | log::Level::Trace => ("", ""),
28-
};
29-
out.finish(format_args!(
30-
"{}{}: {}{}",
31-
color,
32-
record.level(),
33-
if verbose {
34-
format!("({}) {}", record.target(), message)
35-
} else {
36-
message.to_string()
37-
},
38-
reset
39-
))
40-
})
41-
.level(if verbose {
42-
LevelFilter::Info
43-
} else {
44-
LevelFilter::Warn
16+
let cwd = std::env::current_dir()?;
17+
let workspaces: Vec<_> = cmd_args
18+
.workspace
19+
.into_iter()
20+
.map(|workspace| {
21+
if workspace.is_absolute() {
22+
workspace
23+
} else {
24+
cwd.join(workspace)
25+
}
4526
})
46-
.chain(std::io::stderr());
47-
48-
if let Err(e) = logger.apply() {
49-
eprintln!("Failed to apply logger: {:?}", e);
50-
}
27+
.collect();
28+
let main_path = workspaces
29+
.first()
30+
.ok_or("Failed to load workspace")?
31+
.clone();
5132

52-
let analysis = match init::load_workspace(workspace.clone(), cmd_args.config, cmd_args.ignore) {
33+
let analysis = match init::load_workspace(
34+
main_path.clone(),
35+
workspaces.clone(),
36+
cmd_args.config,
37+
cmd_args.ignore,
38+
) {
5339
Some(analysis) => analysis,
5440
None => {
5541
return Err("Failed to load workspace".into());
@@ -58,7 +44,7 @@ pub async fn run_check(cmd_args: CmdArgs) -> Result<(), Box<dyn Error + Sync + S
5844

5945
let files = analysis.compilation.get_db().get_vfs().get_all_file_ids();
6046
let db = analysis.compilation.get_db();
61-
let need_check_files = get_need_check_ids(db, files, &workspace);
47+
let need_check_files = get_need_check_ids(db, files, &workspaces);
6248

6349
let (sender, receiver) = tokio::sync::mpsc::channel(100);
6450
let analysis = Arc::new(analysis);
@@ -76,7 +62,7 @@ pub async fn run_check(cmd_args: CmdArgs) -> Result<(), Box<dyn Error + Sync + S
7662
let exit_code = output_result(
7763
need_check_files.len(),
7864
db,
79-
workspace,
65+
main_path,
8066
receiver,
8167
cmd_args.output_format,
8268
cmd_args.output,

crates/emmylua_doc_cli/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,8 @@ walkdir.workspace = true
2929
tera.workspace = true
3030
include_dir.workspace = true
3131
clap.workspace = true
32+
log.workspace = true
33+
fern.workspace = true
34+
35+
[[bin]]
36+
name = "emmylua_doc_cli"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
use emmylua_doc_cli::{run_doc_cli, CmdArgs, Parser};
2+
3+
fn main() -> Result<(), Box<dyn std::error::Error>> {
4+
let cmd_args = CmdArgs::parse();
5+
run_doc_cli(cmd_args)
6+
}

crates/emmylua_doc_cli/src/cmd_args.rs

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,42 @@ use std::path::PathBuf;
33

44
#[derive(Debug, Parser)]
55
pub struct CmdArgs {
6-
/// The path of the lua project
7-
#[arg(long, short, num_args = 1..)]
6+
/// Configuration file paths.
7+
/// If not provided, both ".emmyrc.json" and ".luarc.json" will be searched in the workspace
8+
/// directory
9+
#[arg(short, long, value_delimiter = ',')]
10+
pub config: Option<Vec<PathBuf>>,
11+
12+
/// Deprecated, use [WORKSPACE] instead
13+
#[arg(short, long, num_args = 1..)]
814
pub input: Vec<PathBuf>,
915

10-
/// Format of the output, default is Markdown
11-
#[arg(long, short, default_value = "markdown")]
12-
pub format: Format,
16+
/// Path to the workspace directory
17+
#[arg(num_args = 1..)]
18+
pub workspace: Vec<PathBuf>,
19+
20+
/// Comma separated list of ignore patterns.
21+
/// Patterns must follow glob syntax
22+
#[arg(long, value_delimiter = ',')]
23+
pub ignore: Option<Vec<String>>,
24+
25+
/// Specify output format
26+
#[arg(
27+
long,
28+
short = 'f',
29+
default_value = "markdown",
30+
value_enum,
31+
ignore_case = true
32+
)]
33+
pub output_format: Format,
1334

14-
/// The output path of the docs file
35+
/// Deprecated, use --output-format instead
36+
#[arg(long, value_enum, ignore_case = true)]
37+
pub format: Option<Format>,
38+
39+
/// Specify output destination (can be stdout when output_format is json)
1540
#[arg(long, short, default_value = "./output")]
16-
pub output: PathBuf,
41+
pub output: OutputDestination,
1742

1843
/// The path of the override template
1944
#[arg(long)]
@@ -25,22 +50,31 @@ pub struct CmdArgs {
2550
/// The path of the mixin md file
2651
#[arg(long)]
2752
pub mixin: Option<PathBuf>,
53+
54+
/// Verbose output
55+
#[arg(long)]
56+
pub verbose: bool,
2857
}
2958

3059
#[derive(Debug, Clone, Eq, PartialEq, ValueEnum)]
3160
pub enum Format {
32-
Markdown,
3361
Json,
62+
Markdown,
3463
}
3564

36-
impl std::str::FromStr for Format {
37-
type Err = &'static str;
65+
#[allow(unused)]
66+
#[derive(Debug, Clone)]
67+
pub enum OutputDestination {
68+
Stdout,
69+
File(PathBuf),
70+
}
3871

72+
impl std::str::FromStr for OutputDestination {
73+
type Err = String;
3974
fn from_str(s: &str) -> Result<Self, Self::Err> {
40-
match s.to_lowercase().as_ref() {
41-
"markdown" => Ok(Self::Markdown),
42-
"json" => Ok(Self::Json),
43-
_ => Err("Invalid format, must be one of markdown, json"),
75+
match s.to_lowercase().as_str() {
76+
"stdout" => Ok(OutputDestination::Stdout),
77+
_ => Ok(OutputDestination::File(PathBuf::from(s))),
4478
}
4579
}
4680
}

0 commit comments

Comments
 (0)