Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
22 changes: 3 additions & 19 deletions crates/pgls_cli/src/commands/check.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use crate::cli_options::CliOptions;
use crate::commands::get_files_to_process_with_cli_options;
use crate::commands::{get_files_to_process_with_cli_options, read_stdin_payload};
use crate::diagnostics::DatabaseUnavailable;
use crate::execute::{StdinPayload, run_files, run_stdin};
use crate::execute::{run_files, run_stdin};
use crate::reporter::Report;
use crate::{CliDiagnostic, CliSession, VcsIntegration};
use crate::{ExecutionConfig, ExecutionMode, VcsTargeting};
use pgls_configuration::PartialConfiguration;
use pgls_console::Console;
use pgls_diagnostics::{Error, category};
use pgls_fs::FileSystem;
use pgls_workspace::DynRef;
Expand Down Expand Up @@ -49,7 +48,7 @@ pub fn check(
let execution = ExecutionConfig::new(mode, max_diagnostics);

if let Some(stdin_path) = args.stdin_file_path.as_deref() {
let payload = read_stdin_payload(stdin_path, session.console())?;
let payload = read_stdin_payload(stdin_path, session.console(), "check")?;
run_stdin(&mut session, &execution, payload)
} else {
let database_error = check_database_connection(&session)?;
Expand Down Expand Up @@ -99,21 +98,6 @@ fn resolve_paths(
Ok(paths)
}

fn read_stdin_payload(
path: &str,
console: &mut dyn Console,
) -> Result<StdinPayload, CliDiagnostic> {
let input_code = console.read();
if let Some(input_code) = input_code {
Ok(StdinPayload {
path: path.into(),
content: input_code,
})
} else {
Err(CliDiagnostic::missing_argument("stdin", "check"))
}
}

fn enforce_exit_codes(cli_options: &CliOptions, payload: &Report) -> Result<(), CliDiagnostic> {
let traversal = payload.traversal.as_ref();
let processed = traversal.map_or(0, |t| t.changed + t.unchanged);
Expand Down
28 changes: 24 additions & 4 deletions crates/pgls_cli/src/commands/format.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::cli_options::CliOptions;
use crate::commands::get_files_to_process_with_cli_options;
use crate::execute::run_files;
use crate::commands::{get_files_to_process_with_cli_options, read_stdin_payload};
use crate::execute::{run_files, run_stdin};
use crate::reporter::Report;
use crate::{CliDiagnostic, CliSession, VcsIntegration};
use crate::{ExecutionConfig, ExecutionMode, VcsTargeting};
Expand All @@ -14,6 +14,7 @@ pub struct FormatArgs {
pub configuration: Option<PartialConfiguration>,
pub paths: Vec<OsString>,
pub write: bool,
pub stdin_file_path: Option<String>,
pub staged: bool,
pub changed: bool,
pub since: Option<String>,
Expand All @@ -29,8 +30,6 @@ pub fn format(
let configuration = session.prepare_with_config(cli_options, args.configuration.clone())?;
session.setup_workspace(configuration.clone(), VcsIntegration::Enabled)?;

let paths = resolve_paths(session.fs(), &configuration, &args)?;

let vcs = VcsTargeting {
staged: args.staged,
changed: args.changed,
Expand All @@ -42,6 +41,12 @@ pub fn format(
};
let execution = ExecutionConfig::new(mode, u32::MAX);

if let Some(stdin_path) = args.stdin_file_path.as_deref() {
let payload = read_stdin_payload(stdin_path, session.console(), "format")?;
return run_stdin(&mut session, &execution, payload);
}

let paths = resolve_paths(session.fs(), &configuration, &args)?;
let report: Report = run_files(&mut session, &execution, paths)?;

let exit_result = enforce_exit_codes(cli_options, &report);
Expand Down Expand Up @@ -92,6 +97,21 @@ fn enforce_exit_codes(cli_options: &CliOptions, payload: &Report) -> Result<(),
}

fn validate_args(args: &FormatArgs) -> Result<(), CliDiagnostic> {
if args.stdin_file_path.is_some() {
let incompatible = [
("PATH", !args.paths.is_empty()),
("write", args.write),
("staged", args.staged),
("changed", args.changed),
];
if let Some((argument, _)) = incompatible.iter().find(|(_, is_set)| *is_set) {
return Err(CliDiagnostic::incompatible_arguments(
"stdin-file-path",
*argument,
));
}
}

if args.since.is_some() {
if !args.changed {
return Err(CliDiagnostic::incompatible_arguments("since", "changed"));
Expand Down
26 changes: 26 additions & 0 deletions crates/pgls_cli/src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use crate::changed::{get_changed_files, get_staged_files};
use crate::cli_options::{CliOptions, CliReporter, ColorsArg, cli_options};
use crate::execute::StdinPayload;
use crate::logging::LoggingKind;
use crate::{CliDiagnostic, LoggingLevel, VERSION};
use bpaf::Bpaf;
use pgls_configuration::{PartialConfiguration, partial_configuration};
use pgls_console::Console;
use pgls_fs::FileSystem;
use pgls_workspace::DynRef;
use std::ffi::OsString;
Expand Down Expand Up @@ -87,6 +89,15 @@ pub enum PgLSCommand {
#[bpaf(long("write"), switch)]
write: bool,

/// Use this option when you want to format code piped from `stdin`, and print the output to `stdout`.
///
/// The file doesn't need to exist on disk. The path is used to resolve the configuration
/// (e.g. ignored files) and to label diagnostics, which are printed to `stderr`.
///
/// Example: `echo 'select 1' | postgres-language-server format --stdin-file-path=test.sql`
#[bpaf(long("stdin-file-path"), argument("PATH"), hide_usage)]
stdin_file_path: Option<String>,

/// When set to true, only the files that have been staged (the ones prepared to be committed)
/// will be formatted. This option should be used when working locally.
#[bpaf(long("staged"), switch)]
Expand Down Expand Up @@ -373,6 +384,21 @@ pub(crate) fn get_files_to_process_with_cli_options(
}
}

/// Reads the code piped to `stdin` for the given virtual file path.
pub(crate) fn read_stdin_payload(
path: &str,
console: &mut dyn Console,
subcommand: &str,
) -> Result<StdinPayload, CliDiagnostic> {
match console.read() {
Some(content) => Ok(StdinPayload {
path: path.into(),
content,
}),
None => Err(CliDiagnostic::missing_argument("stdin", subcommand)),
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
1 change: 0 additions & 1 deletion crates/pgls_cli/src/execute/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ pub fn run_files(
}

pub struct StdinPayload {
#[allow(dead_code)]
pub path: PathBuf,
pub content: String,
}
Expand Down
14 changes: 14 additions & 0 deletions crates/pgls_cli/src/execute/process_file/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ fn format_with_guard<'ctx>(
return Ok(FileStatus::Changed);
}

let only_layout_between_statements = format_result.statements.is_empty();

for stmt in format_result.statements {
let diff = pgls_text_edit::TextEdit::from_unicode_words(&stmt.original, &stmt.formatted);
let start_byte: usize = stmt.range.start().into();
Expand All @@ -76,6 +78,18 @@ fn format_with_guard<'ctx>(
}));
}

// Only the blank lines between statements change: show the whole file.
if only_layout_between_statements && format_result.original != format_result.formatted {
diagnostics.push(pgls_diagnostics::Error::from(FormatDiffDiagnostic {
file_name: path.display().to_string(),
diff: pgls_text_edit::TextEdit::from_unicode_words(
&format_result.original,
&format_result.formatted,
),
start_line: 0,
}));
}

if diagnostics.is_empty() {
Ok(FileStatus::Unchanged)
} else {
Expand Down
65 changes: 61 additions & 4 deletions crates/pgls_cli/src/execute/stdin.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,70 @@
use crate::execute::StdinPayload;
use crate::execute::config::ExecutionConfig;
use crate::execute::config::{ExecutionConfig, ExecutionMode};
use crate::{CliDiagnostic, CliSession};
use pgls_console::{ConsoleExt, markup};
use pgls_diagnostics::{DiagnosticExt, Error, PrintDiagnostic, category};
use pgls_fs::PgLSPath;
use pgls_workspace::features::format::PullFileFormattingParams;
use pgls_workspace::workspace::{FileGuard, OpenFileParams};

pub(crate) fn process(
session: &mut CliSession,
_config: &ExecutionConfig,
config: &ExecutionConfig,
payload: StdinPayload,
) -> Result<(), CliDiagnostic> {
session.console().append(markup! {{payload.content}});
Ok(())
match config.mode {
ExecutionMode::Check { .. } => {
session.console().append(markup! {{payload.content}});
Ok(())
}
ExecutionMode::Format { .. } => format(session, payload),
}
}

/// Formats the content read from stdin and prints only the formatted code to stdout.
///
/// Diagnostics are printed to stderr. Statements that could not be formatted are kept as
/// they are, so stdout always contains the best-effort output.
fn format(session: &mut CliSession, payload: StdinPayload) -> Result<(), CliDiagnostic> {
let pgls_path = PgLSPath::new(&payload.path);
let workspace = session.workspace();

let result = {
let guard = FileGuard::open(
workspace,
OpenFileParams {
path: pgls_path.clone(),
version: 0,
content: payload.content.clone(),
},
)?;
guard.pull_file_formatting(PullFileFormattingParams {
path: pgls_path,
range: None,
})?
};

// The workspace returns an empty result for an ignored file or a disabled formatter.
let output = if result.original == payload.content {
result.formatted
} else {
payload.content.clone()
};

let console = session.console();
console.append(markup! {{output}});

if result.diagnostics.is_empty() {
return Ok(());
}

let file_name = payload.path.display().to_string();
for diagnostic in result.diagnostics {
let diagnostic = Error::from(diagnostic)
.with_file_path(&file_name)
.with_file_source_code(&payload.content);
console.error(markup! {{PrintDiagnostic::simple(&diagnostic)}});
}

Err(CliDiagnostic::check_error(category!("format")))
}
2 changes: 2 additions & 0 deletions crates/pgls_cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ impl<'app> CliSession<'app> {
configuration,
paths,
write,
stdin_file_path,
staged,
changed,
since,
Expand All @@ -107,6 +108,7 @@ impl<'app> CliSession<'app> {
configuration,
paths,
write,
stdin_file_path,
staged,
changed,
since,
Expand Down
Loading
Loading