Skip to content

Commit dfe465b

Browse files
feat: unify configuration reading
fix ast-grep#1557
1 parent 920d45b commit dfe465b

File tree

6 files changed

+59
-74
lines changed

6 files changed

+59
-74
lines changed

crates/cli/src/lib.rs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ mod verify;
1111

1212
use anyhow::Result;
1313
use clap::{Parser, Subcommand};
14+
use std::path::PathBuf;
1415

1516
use completions::{run_shell_completion, CompletionsArg};
17+
use config::ProjectConfig;
1618
use lsp::{run_language_server, LspArg};
1719
use new::{run_create_new, NewArg};
18-
use run::{register_custom_language_if_is_run, run_with_pattern, RunArg};
20+
use run::{run_with_pattern, RunArg};
1921
use scan::{run_with_config, ScanArg};
2022
use utils::exit_with_error;
2123
use verify::{run_test_rule, TestArg};
@@ -39,6 +41,9 @@ Search and Rewrite code at large scale using AST pattern.
3941
struct App {
4042
#[clap(subcommand)]
4143
command: Commands,
44+
/// Path to ast-grep root config, default is sgconfig.yml.
45+
#[clap(short, long, global = true, value_name = "CONFIG_FILE")]
46+
config: Option<PathBuf>,
4247
}
4348

4449
#[derive(Subcommand)]
@@ -82,18 +87,30 @@ fn try_default_run(args: &[String]) -> Result<Option<RunArg>> {
8287
// this wrapper function is for testing
8388
pub fn main_with_args(args: impl Iterator<Item = String>) -> Result<()> {
8489
let args: Vec<_> = args.collect();
85-
register_custom_language_if_is_run(&args)?;
90+
let mut config = None;
91+
for i in 0..args.len() {
92+
if args[i] != "-c" && args[i] != "--config" {
93+
continue;
94+
}
95+
if i + 1 >= args.len() {
96+
return Err(anyhow::anyhow!("missing config file after -c"));
97+
}
98+
let config_file = (&args[i + 1]).into();
99+
config = Some(config_file);
100+
}
101+
let project = ProjectConfig::setup(config)?;
102+
// register_custom_language_if_is_run(&args)?;
86103
if let Some(arg) = try_default_run(&args)? {
87104
return run_with_pattern(arg);
88105
}
89106

90107
let app = App::try_parse_from(args)?;
91108
match app.command {
92109
Commands::Run(arg) => run_with_pattern(arg),
93-
Commands::Scan(arg) => run_with_config(arg),
94-
Commands::Test(arg) => run_test_rule(arg),
95-
Commands::New(arg) => run_create_new(arg),
96-
Commands::Lsp(arg) => run_language_server(arg),
110+
Commands::Scan(arg) => run_with_config(arg, project),
111+
Commands::Test(arg) => run_test_rule(arg, project),
112+
Commands::New(arg) => run_create_new(arg, project),
113+
Commands::Lsp(arg) => run_language_server(arg, project),
97114
Commands::Completions(arg) => run_shell_completion::<App>(arg),
98115
Commands::Docs => todo!("todo, generate rule docs based on current config"),
99116
}
@@ -149,7 +166,7 @@ mod test_cli {
149166
fn test_no_arg_run() {
150167
let ret = main_with_args(["sg".to_owned()].into_iter());
151168
let err = ret.unwrap_err();
152-
assert!(err.to_string().contains("sg <COMMAND>"));
169+
assert!(err.to_string().contains("sg [OPTIONS] <COMMAND>"));
153170
}
154171
#[test]
155172
fn test_default_subcommand() {
@@ -185,11 +202,11 @@ mod test_cli {
185202
ok("run -p test --globs '*.js' --globs '*.ts'");
186203
ok("run -p fubuki -j8");
187204
ok("run -p test --threads 12");
205+
ok("run -p test -l rs -c config.yml"); // global config arg
188206
error("run test");
189207
error("run --debug-query test"); // missing lang
190208
error("run -r Test dir");
191209
error("run -p test -i --json dir"); // conflict
192-
error("run -p test -l rs -c always"); // no color shortcut
193210
error("run -p test -U");
194211
error("run -p test --update-all");
195212
error("run -p test --strictness not");

crates/cli/src/lsp.rs

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,14 @@ use crate::utils::ErrorContext as EC;
33
use anyhow::{Context, Result};
44
use ast_grep_lsp::{Backend, LspService, Server};
55
use clap::Args;
6-
use std::path::PathBuf;
76

87
#[derive(Args)]
9-
pub struct LspArg {
10-
/// Path to ast-grep root config, default is sgconfig.yml.
11-
#[clap(short, long, value_name = "CONFIG_FILE")]
12-
config: Option<PathBuf>,
13-
}
8+
pub struct LspArg {}
149

15-
async fn run_language_server_impl(arg: LspArg) -> Result<()> {
10+
async fn run_language_server_impl(_arg: LspArg, project: Result<ProjectConfig>) -> Result<()> {
1611
// env_logger::init();
1712
// TODO: move this error to client
18-
let project_config = ProjectConfig::setup(arg.config.clone())??;
13+
let project_config = project?;
1914
let stdin = tokio::io::stdin();
2015
let stdout = tokio::io::stdout();
2116
let config_result = project_config.find_rules(Default::default());
@@ -35,12 +30,12 @@ async fn run_language_server_impl(arg: LspArg) -> Result<()> {
3530
Ok(())
3631
}
3732

38-
pub fn run_language_server(arg: LspArg) -> Result<()> {
33+
pub fn run_language_server(arg: LspArg, project: Result<ProjectConfig>) -> Result<()> {
3934
tokio::runtime::Builder::new_multi_thread()
4035
.enable_all()
4136
.build()
4237
.context(EC::StartLanguageServer)?
43-
.block_on(async { run_language_server_impl(arg).await })
38+
.block_on(async { run_language_server_impl(arg, project).await })
4439
}
4540

4641
#[cfg(test)]
@@ -50,7 +45,7 @@ mod test {
5045
#[test]
5146
#[ignore = "test lsp later"]
5247
fn test_lsp_start() {
53-
let arg = LspArg { config: None };
54-
assert!(run_language_server(arg).is_err())
48+
let arg = LspArg {};
49+
assert!(run_language_server(arg, Err(anyhow::anyhow!("error"))).is_err())
5550
}
5651
}

crates/cli/src/new.rs

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ pub struct NewArg {
2929
/// Please see the command description for the what arguments are required.
3030
#[arg(short, long, global = true)]
3131
yes: bool,
32-
33-
/// Path to ast-grep root config, default is sgconfig.yml.
34-
#[clap(short, long, global = true, value_name = "CONFIG_FILE")]
35-
config: Option<PathBuf>,
3632
}
3733

3834
fn create_dir(project_dir: &Path, dir: &str) -> Result<PathBuf> {
@@ -148,12 +144,11 @@ impl Display for Entity {
148144
}
149145
}
150146

151-
pub fn run_create_new(mut arg: NewArg) -> Result<()> {
152-
let project_config = ProjectConfig::setup(arg.config.clone())?;
147+
pub fn run_create_new(mut arg: NewArg, project: Result<ProjectConfig>) -> Result<()> {
153148
if let Some(entity) = arg.entity.take() {
154-
run_create_entity(entity, arg, project_config)
149+
run_create_entity(entity, arg, project)
155150
} else {
156-
ask_entity_type(arg, project_config)
151+
ask_entity_type(arg, project)
157152
}
158153
}
159154

@@ -369,35 +364,34 @@ mod test {
369364
name: None,
370365
lang: None,
371366
yes: true,
372-
config: None,
373367
};
374368
create_new_project(arg, tempdir)?;
375369
assert!(tempdir.join("sgconfig.yml").exists());
376370
Ok(())
377371
}
378372

379373
fn create_rule(temp: &Path) -> Result<()> {
374+
let project = ProjectConfig::setup(Some(temp.join("sgconfig.yml")))?;
380375
let arg = NewArg {
381376
entity: Some(Entity::Rule),
382377
name: Some("test-rule".into()),
383378
lang: Some(SupportLang::Rust.into()),
384379
yes: true,
385-
config: Some(temp.join("sgconfig.yml")),
386380
};
387-
run_create_new(arg)?;
381+
run_create_new(arg, project)?;
388382
assert!(temp.join("rules/test-rule.yml").exists());
389383
Ok(())
390384
}
391385

392386
fn create_util(temp: &Path) -> Result<()> {
387+
let project = ProjectConfig::setup(Some(temp.join("sgconfig.yml")))?;
393388
let arg = NewArg {
394389
entity: Some(Entity::Util),
395390
name: Some("test-utils".into()),
396391
lang: Some(SupportLang::Rust.into()),
397392
yes: true,
398-
config: Some(temp.join("sgconfig.yml")),
399393
};
400-
run_create_new(arg)?;
394+
run_create_new(arg, project)?;
401395
assert!(temp.join("utils/test-utils.yml").exists());
402396
Ok(())
403397
}

crates/cli/src/run.rs

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,13 @@ use ast_grep_language::Language;
77
use clap::{builder::PossibleValue, Parser, ValueEnum};
88
use ignore::WalkParallel;
99

10-
use crate::config::ProjectConfig;
1110
use crate::lang::SgLang;
1211
use crate::print::{ColoredPrinter, Diff, Heading, InteractivePrinter, JSONPrinter, Printer};
1312
use crate::utils::ErrorContext as EC;
1413
use crate::utils::{filter_file_pattern, ContextArgs, InputArgs, MatchUnit, OutputArgs};
1514
use crate::utils::{DebugFormat, FileTrace, RunTrace};
1615
use crate::utils::{Items, PathWorker, StdInWorker, Worker};
1716

18-
// NOTE: have to register custom lang before clap read arg
19-
// RunArg has a field of SgLang
20-
pub fn register_custom_language_if_is_run(args: &[String]) -> Result<()> {
21-
let Some(arg) = args.get(1) else {
22-
return Ok(());
23-
};
24-
if arg.starts_with('-') || arg == "run" {
25-
_ = ProjectConfig::setup(None)?;
26-
}
27-
Ok(())
28-
}
29-
3017
fn lang_help() -> String {
3118
format!(
3219
"The language of the pattern. Supported languages are:\n{:?}",

crates/cli/src/scan.rs

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,6 @@ type AstGrep = ast_grep_core::AstGrep<StrDoc<SgLang>>;
2525

2626
#[derive(Args)]
2727
pub struct ScanArg {
28-
/// Path to ast-grep root config, default is sgconfig.yml.
29-
#[clap(short, long, value_name = "CONFIG_FILE")]
30-
config: Option<PathBuf>,
31-
3228
/// Scan the codebase with the single rule located at the path RULE_FILE.
3329
///
3430
/// It is useful to run single rule without project setup or sgconfig.yml.
@@ -67,15 +63,15 @@ pub struct ScanArg {
6763
context: ContextArgs,
6864
}
6965

70-
pub fn run_with_config(arg: ScanArg) -> Result<()> {
66+
pub fn run_with_config(arg: ScanArg, project: Result<ProjectConfig>) -> Result<()> {
7167
let context = arg.context.get();
7268
if let Some(_format) = &arg.format {
7369
let printer = CloudPrinter::stdout();
74-
return run_scan(arg, printer);
70+
return run_scan(arg, printer, project);
7571
}
7672
if let Some(json) = arg.output.json {
7773
let printer = JSONPrinter::stdout(json);
78-
return run_scan(arg, printer);
74+
return run_scan(arg, printer, project);
7975
}
8076
let printer = ColoredPrinter::stdout(arg.output.color)
8177
.style(arg.report_style)
@@ -84,20 +80,23 @@ pub fn run_with_config(arg: ScanArg) -> Result<()> {
8480
if interactive {
8581
let from_stdin = arg.input.stdin;
8682
let printer = InteractivePrinter::new(printer, arg.output.update_all, from_stdin)?;
87-
run_scan(arg, printer)
83+
run_scan(arg, printer, project)
8884
} else {
89-
run_scan(arg, printer)
85+
run_scan(arg, printer, project)
9086
}
9187
}
9288

93-
fn run_scan<P: Printer + 'static>(arg: ScanArg, printer: P) -> Result<()> {
94-
let project_config = ProjectConfig::setup(arg.config.clone())?;
89+
fn run_scan<P: Printer + 'static>(
90+
arg: ScanArg,
91+
printer: P,
92+
project: Result<ProjectConfig>,
93+
) -> Result<()> {
9594
if arg.input.stdin {
9695
let worker = ScanWithRule::try_new(arg, printer)?;
9796
// TODO: report a soft error if rules have different languages
9897
worker.run_std_in()
9998
} else {
100-
let worker = ScanWithConfig::try_new(arg, printer, project_config)?;
99+
let worker = ScanWithConfig::try_new(arg, printer, project)?;
101100
worker.run_path()
102101
}
103102
}
@@ -357,7 +356,6 @@ rule:
357356

358357
fn default_scan_arg() -> ScanArg {
359358
ScanArg {
360-
config: None,
361359
rule: None,
362360
inline_rules: None,
363361
report_style: ReportStyle::Rich,
@@ -404,11 +402,9 @@ rule:
404402
.write_all("fn test() { Some(123) }".as_bytes())
405403
.unwrap();
406404
file.sync_all().unwrap();
407-
let arg = ScanArg {
408-
config: Some(dir.path().join("sgconfig.yml")),
409-
..default_scan_arg()
410-
};
411-
assert!(run_with_config(arg).is_ok());
405+
let project_config = ProjectConfig::setup(Some(dir.path().join("sgconfig.yml"))).unwrap();
406+
let arg = default_scan_arg();
407+
assert!(run_with_config(arg, project_config).is_ok());
412408
}
413409

414410
#[test]
@@ -418,7 +414,7 @@ rule:
418414
inline_rules: Some(inline_rules),
419415
..default_scan_arg()
420416
};
421-
assert!(run_with_config(arg).is_ok());
417+
assert!(run_with_config(arg, Err(anyhow::anyhow!("not found"))).is_ok());
422418
}
423419

424420
#[test]
@@ -429,7 +425,7 @@ rule:
429425
inline_rules: Some(inline_rules),
430426
..default_scan_arg()
431427
};
432-
assert!(run_with_config(arg).is_ok());
428+
assert!(run_with_config(arg, Err(anyhow::anyhow!("not found"))).is_ok());
433429
}
434430

435431
// baseline test for coverage
@@ -440,7 +436,7 @@ rule:
440436
inline_rules: Some(inline_rules),
441437
..default_scan_arg()
442438
};
443-
let err = run_with_config(arg).expect_err("should error");
439+
let err = run_with_config(arg, Err(anyhow::anyhow!("not found"))).expect_err("should error");
444440
assert!(err.is::<EC>());
445441
assert_eq!(err.to_string(), "Cannot parse rule INLINE_RULES");
446442
}

crates/cli/src/verify.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,6 @@ fn verify_test_case_simple<'a>(
160160

161161
#[derive(Args)]
162162
pub struct TestArg {
163-
/// Path to the root ast-grep config YAML
164-
#[clap(short, long)]
165-
config: Option<PathBuf>,
166163
/// the directories to search test YAML files
167164
#[clap(short, long)]
168165
test_dir: Option<PathBuf>,
@@ -186,8 +183,8 @@ pub struct TestArg {
186183
filter: Option<Regex>,
187184
}
188185

189-
pub fn run_test_rule(arg: TestArg) -> Result<()> {
190-
let project = ProjectConfig::setup(arg.config.clone())??;
186+
pub fn run_test_rule(arg: TestArg, project: Result<ProjectConfig>) -> Result<()> {
187+
let project = project?;
191188
if arg.interactive {
192189
let reporter = InteractiveReporter {
193190
output: std::io::stdout(),
@@ -307,15 +304,14 @@ rule:
307304
#[test]
308305
fn test_run_verify_error() {
309306
let arg = TestArg {
310-
config: None,
311307
interactive: false,
312308
skip_snapshot_tests: true,
313309
snapshot_dir: None,
314310
test_dir: None,
315311
update_all: false,
316312
filter: None,
317313
};
318-
assert!(run_test_rule(arg).is_err());
314+
assert!(run_test_rule(arg, Err(anyhow!("error"))).is_err());
319315
}
320316
const TRANSFORM_TEXT: &str = "
321317
transform:

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy