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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ plugin.wasm
!/plugins/*/plugin.wasm
/crates/*/target
/plugins/workspace/target

# Computed search diffs.
/.cache/diffr/
1 change: 1 addition & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ include = [

[dependencies]
diffr-core = { path = "crates/diffr-core", version = "0.1.16" }
sha2 = "0.10"
tempfile = "3.27.0"
gix = { version = "0.88", default-features = false, features = ["parallel", "revision", "status", "blob-diff", "attributes", "sha1"] }
clap = { version = "4.0.0", features = ["cargo", "derive", "env", "wrap_help", "string"] }
typed-arena = "2.0.2"
Expand Down
4 changes: 4 additions & 0 deletions crates/diffr-core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
pub mod migrate;
pub(crate) mod prune;
pub(crate) mod query;
pub mod storage;
pub mod store;
use crate::hash::DftHashMap;
use crate::options::DiffOptions;
Expand Down Expand Up @@ -56,6 +57,9 @@ pub struct Config {
/// Limits on the structural comparison itself.
#[serde(default)]
pub diff: DiffConfig,
/// Cache for computed diffs.
#[serde(default)]
pub storage: storage::StoreConfig,
}

impl Default for Config {
Expand Down
3 changes: 3 additions & 0 deletions crates/diffr-core/src/config/default.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,6 @@ name = "default-dark"
byte_limit = 1000000
graph_limit = 3000000
parse_error_limit = 0

[storage]
path = ".cache/diffr"
18 changes: 18 additions & 0 deletions crates/diffr-core/src/config/storage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//! Where search keeps computed diffs.
use serde::{Deserialize, Serialize};
use std::path::PathBuf;

#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(default, deny_unknown_fields)]
pub struct StoreConfig {
/// Directory for computed diffs, relative to the source repository unless absolute.
#[schemars(title = "Diff storage directory", extend("x-group" = "Storage"))]
pub path: PathBuf,
}
impl Default for StoreConfig {
fn default() -> Self {
Self {
path: ".cache/diffr".into(),
}
}
}
47 changes: 46 additions & 1 deletion crates/diffr-core/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::parse::syntax::{self, init_next_prev};
use crate::parse::tree_sitter_parser as tsp;
use crate::summary::{DiffResult, FallbackCause, FileContent, FileFormat, Highlight};
use humansize::{format_size, FormatSizeOptions, BINARY};
use std::{env, fmt, path::Path};
use std::{env, fmt, num::NonZeroU32, path::Path};
use typed_arena::Arena;

/// The fallback reason for a file diffed by line because of what it is.
Expand Down Expand Up @@ -97,6 +97,51 @@ impl DiffResult {
) -> Result<Self, QueryConflict> {
diff_file_content(params, path, lhs, rhs, options, &[])
}
/// Like `from_sources_with_options`, but also parses identical sources so
/// search can show their folds.
pub fn from_sources_with_context(
path: &str,
lhs: &str,
rhs: &str,
params: &Params,
options: &DiffOptions,
) -> Result<Self, QueryConflict> {
let mut diff = Self::from_sources_with_options(path, lhs, rhs, params, options)?;
if lhs == rhs && options.by_line.is_none() && lhs.len() <= options.byte_limit {
if let Some(language) = guess(Path::new(path), lhs, &[]) {
let config = params.language(language);
let tree = tsp::to_tree(lhs, config.parser);
let arena = Arena::new();
let (nodes, _) =
tsp::to_syntax(&tree, lhs, &arena, config, false).map_err(|conflict| {
QueryConflict {
path: path.to_owned(),
side: Side::Left,
conflict,
}
})?;
folds::unmatched(&nodes, &mut diff.lhs_folds);
let count = diff.lhs_folds.len();
for (i, fold) in diff.lhs_folds.iter_mut().enumerate() {
let lhs = NonZeroU32::new(i as u32 + 1).unwrap();
let rhs = NonZeroU32::new((count + i) as u32 + 1).unwrap();
fold.syntax_id = lhs;
fold.match_kind = folds::FoldMatch::Matched { opposite: rhs };
diff.rhs_folds.push(folds::Fold {
tags: fold.tags.clone(),
relations: fold.relations.clone(),
range: fold.range,
indent: fold.indent,
syntax: fold.syntax,
syntax_id: rhs,
match_kind: folds::FoldMatch::Matched { opposite: lhs },
placeholder: String::new(),
});
}
}
}
Ok(diff)
}
}
pub fn diff_file_content(
params: &Params,
Expand Down
5 changes: 3 additions & 2 deletions crates/diffr-core/src/pairing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub enum Pairing<T> {

impl<T> Pairing<T> {
/// The same sides, each value transformed by `f`.
pub(crate) fn map<U>(self, mut f: impl FnMut(T) -> U) -> Pairing<U> {
pub fn map<U>(self, mut f: impl FnMut(T) -> U) -> Pairing<U> {
match self {
Self::Both { lhs, rhs } => Pairing::Both {
lhs: f(lhs),
Expand All @@ -42,7 +42,7 @@ impl<T> Pairing<T> {
}

/// Every side that exists, lhs first.
pub(crate) fn sides(&self) -> Vec<&T> {
pub fn sides(&self) -> Vec<&T> {
match self {
Self::Both { lhs, rhs } => vec![lhs, rhs],
Self::LeftOnly { lhs } => vec![lhs],
Expand All @@ -52,6 +52,7 @@ impl<T> Pairing<T> {
}

#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
struct PairingRepr<T> {
#[serde(skip_serializing_if = "Option::is_none")]
lhs: Option<T>,
Expand Down
4 changes: 2 additions & 2 deletions crates/diffr-core/src/plugin/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ impl PluginsConfig {
pub struct ShapeConfig {
/// The plugins in the order they run; each sees the region trees the
/// ones before it left. Every entry is listed exactly once.
pub(crate) order: Vec<String>,
pub order: Vec<String>,
/// Every entry, by reference: `bundled.<name>` or the whole custom name.
pub entries: BTreeMap<String, Entry>,
}
Expand Down Expand Up @@ -736,7 +736,7 @@ impl Default for ShapeConfig {
impl ShapeConfig {
/// Resolve each selected entry, validate its options, fill their defaults,
/// and require every entry to appear in the explicit order exactly once.
pub(crate) fn resolve(&mut self, base: &Path) -> Result<(), ConfigError> {
pub fn resolve(&mut self, base: &Path) -> Result<(), ConfigError> {
let mut identities = BTreeSet::new();
for (name, entry) in &mut self.entries {
let folder = match (name.strip_prefix("bundled."), &entry.path) {
Expand Down
20 changes: 19 additions & 1 deletion crates/diffr-core/src/plugin/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub struct PluginQuery {
}

/// The query files one plugin declares.
pub(crate) type Queries = Vec<PluginQuery>;
pub type Queries = Vec<PluginQuery>;

const BUILTIN_PREFIX: &str = "builtin:";

Expand Down Expand Up @@ -192,6 +192,24 @@ impl Assembly {
}
}

/// Query sources per language, imports included.
pub fn sources(
plugins: &[(String, Queries)],
) -> Result<BTreeMap<String, Vec<(String, String)>>, ConfigError> {
Ok(assemble(plugins)?
.into_iter()
.map(|(language, sources)| {
(
language,
sources
.into_iter()
.map(|source| (source.name, source.text))
.collect(),
)
})
.collect())
}

/// Assemble in plugin order, with imports before their first importer.
pub(crate) fn assemble(
plugins: &[(String, Queries)],
Expand Down
11 changes: 11 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::git::{self, Comparison, FileParams, Operand, Result};
use crate::options::DebugArgs;
use crate::plugin::{Classifier, Pipeline};
use crate::run;
use crate::search::command as search;
use clap::{
error::ErrorKind, parser::ValueSource, ArgGroup, ArgMatches, Args, CommandFactory,
FromArgMatches, Parser, Subcommand, ValueEnum,
Expand Down Expand Up @@ -126,6 +127,8 @@ enum Format {
enum Command {
/// Print saved NDJSON without a repository or terminal frontend
Pprint(PprintArgs),
/// Shape Git-grep hits from stdin as diffs, or pretty-print the results
Search(search::SearchCommand),
/// Show, edit, or open the settings screen for diffr's configuration
Config(ConfigArgs),
#[command(
Expand Down Expand Up @@ -193,6 +196,13 @@ enum ConfigCommand {
},
}

/// A search command's exit code, with its error chain as the message.
fn exit(result: anyhow::Result<()>) -> Result<i32> {
result
.map(|()| 0)
.map_err(|error| format!("{error:#}").into())
}

pub(crate) fn run(runtime: &tokio::runtime::Runtime) -> Result<i32> {
let frontend_args: Vec<OsString> = std::env::args_os().skip(1).collect();
// Git treats an argument before `--` as a revision even when a file shares
Expand All @@ -203,6 +213,7 @@ pub(crate) fn run(runtime: &tokio::runtime::Runtime) -> Result<i32> {
let args = Cli::from_arg_matches(&matches).unwrap_or_else(|error| error.exit());
match &args.command {
Some(Command::Pprint(args)) => return run_pprint(args),
Some(Command::Search(command)) => return exit(search::run(command, runtime)),
Some(Command::Config(config)) => return run_config(config),
Some(Command::Debug(debug)) => {
crate::run_debug(debug.mode(), &Config::default().compile()?);
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ mod options;
mod plugin;
mod pprint;
mod run;
mod search;
mod tags;
mod version;

Expand Down
2 changes: 1 addition & 1 deletion src/plugin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub(crate) mod wasm;
pub(crate) use classify::Classifier;
#[cfg(test)]
pub(crate) use diffr_core::plugin::builtin;
pub(crate) use diffr_core::plugin::{config, cursor, MutationFailed};
pub(crate) use diffr_core::plugin::{config, cursor, queries, MutationFailed};
pub(crate) use wasm::Pipeline;

#[cfg(test)]
Expand Down
Loading
Loading