Skip to content
Open
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
  •  
  •  
  •  
59 changes: 56 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 13 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,20 @@ rust-version = "1.86.0"
[workspace.dependencies]
# supporting crates unrelated to postgres
anyhow = "1.0.92"
biome_console = "=0.5.7"
biome_deserialize = "0.6.0"
biome_deserialize_macros = "0.6.0"
biome_diagnostics = "=0.5.7"
biome_js_factory = "0.5.7"
biome_js_formatter = "0.5.7"
biome_js_syntax = "0.5.7"
biome_rowan = "0.5.7"
biome_string_case = "0.5.8"
biome_json_parser = "=0.5.7"
biome_parser = "=0.5.7"
biome_rowan = "=0.5.7"
biome_string_case = "=0.5.8"
biome_text_edit = "=0.5.7"
biome_text_size = "=0.5.7"
biome_unicode_table = "=0.5.7"
bpaf = { version = "0.9.15", features = ["derive"] }
criterion = "0.5"
crossbeam = "0.8.4"
Expand All @@ -44,7 +51,9 @@ slotmap = "1.0.7"
smallvec = { version = "1.13.2", features = ["union", "const_new", "serde"] }
strum = { version = "0.27.1", features = ["derive"] }
# this will use tokio if available, otherwise async-std
camino = "1.1.9"
convert_case = "0.6.0"
dir-test = "0.4.1"
prost = "0.13.5"
prost-reflect = "0.15.3"
protox = "0.8.0"
Expand Down Expand Up @@ -79,6 +88,8 @@ pgls_lsp = { path = "./crates/pgls_lsp", version = "0.0.0" }
pgls_markup = { path = "./crates/pgls_markup", version = "0.0.0" }
pgls_matcher = { path = "./crates/pgls_matcher", version = "0.0.0" }
pgls_plpgsql_check = { path = "./crates/pgls_plpgsql_check", version = "0.0.0" }
pgls_pretty_print = { path = "./crates/pgls_pretty_print", version = "0.0.0" }
pgls_pretty_print_codegen = { path = "./crates/pgls_pretty_print_codegen", version = "0.0.0" }
pgls_query = { path = "./crates/pgls_query", version = "0.0.0" }
pgls_query_ext = { path = "./crates/pgls_query_ext", version = "0.0.0" }
pgls_query_macros = { path = "./crates/pgls_query_macros", version = "0.0.0" }
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ The following features are implemented:
- Syntax Error Highlighting
- Type-checking (via `EXPLAIN` error insights)
- Linter, inspired by [Squawk](https://squawkhq.com)
- Formatting

Our current focus is on refining and enhancing these core features while building a robust and easily accessible infrastructure. For future plans and opportunities to contribute, please check out the issues and discussions. Any contributions are welcome!

Expand Down
109 changes: 109 additions & 0 deletions crates/pgls_cli/src/commands/format.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use crate::cli_options::CliOptions;
use crate::commands::get_files_to_process_with_cli_options;
use crate::execute::run_files;
use crate::reporter::Report;
use crate::{CliDiagnostic, CliSession, VcsIntegration};
use crate::{ExecutionConfig, ExecutionMode, VcsTargeting};
use pgls_configuration::PartialConfiguration;
use pgls_diagnostics::category;
use pgls_fs::FileSystem;
use pgls_workspace::DynRef;
use std::ffi::OsString;

pub struct FormatArgs {
pub configuration: Option<PartialConfiguration>,
pub paths: Vec<OsString>,
pub write: bool,
pub staged: bool,
pub changed: bool,
pub since: Option<String>,
}

pub fn format(
mut session: CliSession,
cli_options: &CliOptions,
args: FormatArgs,
) -> Result<(), CliDiagnostic> {
validate_args(&args)?;

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,
};

let mode = ExecutionMode::Format {
write: args.write,
vcs,
};
let execution = ExecutionConfig::new(mode, u32::MAX);

let report: Report = run_files(&mut session, &execution, paths)?;

let exit_result = enforce_exit_codes(cli_options, &report);
session.report("format", cli_options, &report)?;
exit_result
}

fn resolve_paths(
fs: &DynRef<'_, dyn FileSystem>,
configuration: &PartialConfiguration,
args: &FormatArgs,
) -> Result<Vec<OsString>, CliDiagnostic> {
let mut paths = get_files_to_process_with_cli_options(
args.since.as_deref(),
args.changed,
args.staged,
fs,
configuration,
)?
.unwrap_or_else(|| args.paths.clone());

if paths.is_empty() {
if let Some(current_dir) = fs.working_directory() {
paths.push(current_dir.into_os_string());
}
}

Ok(paths)
}

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);
let skipped = traversal.map_or(0, |t| t.skipped);

if processed.saturating_sub(skipped) == 0 && !cli_options.no_errors_on_unmatched {
return Err(CliDiagnostic::no_files_processed());
}

let errors = payload.errors;
let category = category!("format");

if errors > 0 {
return Err(CliDiagnostic::check_error(category));
}

Ok(())
}

fn validate_args(args: &FormatArgs) -> Result<(), CliDiagnostic> {
if args.since.is_some() {
if !args.changed {
return Err(CliDiagnostic::incompatible_arguments("since", "changed"));
}
if args.staged {
return Err(CliDiagnostic::incompatible_arguments("since", "staged"));
}
}

if args.changed && args.staged {
return Err(CliDiagnostic::incompatible_arguments("changed", "staged"));
}

Ok(())
}
37 changes: 36 additions & 1 deletion crates/pgls_cli/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub(crate) mod check;
pub(crate) mod clean;
pub(crate) mod daemon;
pub(crate) mod dblint;
pub(crate) mod format;
pub(crate) mod init;
pub(crate) mod version;

Expand Down Expand Up @@ -71,6 +72,39 @@ pub enum PgLSCommand {
paths: Vec<OsString>,
},

/// Formats the requested files.
#[bpaf(command)]
Format {
#[bpaf(external(partial_configuration), hide_usage, optional)]
configuration: Option<PartialConfiguration>,

#[bpaf(external, hide_usage)]
cli_options: CliOptions,

/// Write formatted output back to files
#[bpaf(long("write"), switch)]
write: bool,

/// 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)]
staged: bool,

/// When set to true, only the files that have been changed compared to your `defaultBranch`
/// configuration will be formatted. This option should be used in CI environments.
#[bpaf(long("changed"), switch)]
changed: bool,

/// Use this to specify the base branch to compare against when you're using the --changed
/// flag and the `defaultBranch` is not set in your `postgres-language-server.jsonc`
#[bpaf(long("since"), argument("REF"))]
since: Option<String>,

/// Single file, single path or list of paths
#[bpaf(positional("PATH"), many)]
paths: Vec<OsString>,
},

/// Starts the daemon server process.
#[bpaf(command)]
Start {
Expand Down Expand Up @@ -223,7 +257,8 @@ impl PgLSCommand {
match self {
PgLSCommand::Version(cli_options)
| PgLSCommand::Check { cli_options, .. }
| PgLSCommand::Dblint { cli_options, .. } => Some(cli_options),
| PgLSCommand::Dblint { cli_options, .. }
| PgLSCommand::Format { cli_options, .. } => Some(cli_options),
PgLSCommand::LspProxy { .. }
| PgLSCommand::Start { .. }
| PgLSCommand::Stop
Expand Down
38 changes: 38 additions & 0 deletions crates/pgls_cli/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ use pgls_console::markup;
use pgls_diagnostics::adapters::{BpafError, IoError, SerdeJsonError};
use pgls_diagnostics::{
Advices, Category, Diagnostic, Error, LogCategory, MessageAndDescription, Severity, Visit,
category,
};
use pgls_text_edit::TextEdit;
use pgls_workspace::WorkspaceError;
use std::fmt::{self, Formatter};
use std::process::{ExitCode, Termination};
use std::{env::current_exe, fmt::Debug};

Expand Down Expand Up @@ -447,6 +450,41 @@ impl Termination for CliDiagnostic {
)]
pub struct StdinDiagnostic {}

#[derive(Debug)]
pub struct FormatDiffDiagnostic {
pub file_name: String,
pub diff: TextEdit,
pub start_line: u32,
}

impl Diagnostic for FormatDiffDiagnostic {
fn category(&self) -> Option<&'static Category> {
Some(category!("format"))
}

fn severity(&self) -> Severity {
Severity::Error
}

fn location(&self) -> pgls_diagnostics::Location<'_> {
pgls_diagnostics::Location::builder()
.resource(&self.file_name)
.build()
}

fn description(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
write!(fmt, "Statement could be formatted using `--write`")
}

fn message(&self, fmt: &mut pgls_console::fmt::Formatter<'_>) -> std::io::Result<()> {
fmt.write_str("Statement could be formatted using `--write`")
}

fn advices(&self, visitor: &mut dyn Visit) -> std::io::Result<()> {
visitor.record_diff_with_offset(&self.diff, self.start_line)
}
}

#[cfg(test)]
mod test {
use crate::CliDiagnostic;
Expand Down
Loading