Skip to content

Commit

Permalink
fileset: add alias parser
Browse files Browse the repository at this point in the history
Additionally, adds alias handling to cli_util.

The implementation is mostly copied from the similar parts in
cli/template_parser.
  • Loading branch information
bryceberger committed Nov 24, 2024
1 parent 10c90a5 commit 6b1e2d3
Show file tree
Hide file tree
Showing 9 changed files with 248 additions and 18 deletions.
60 changes: 55 additions & 5 deletions cli/src/cli_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,11 @@ use jj_lib::commit::Commit;
use jj_lib::config::ConfigError;
use jj_lib::config::ConfigNamePathBuf;
use jj_lib::conflicts::ConflictMarkerStyle;
use jj_lib::dsl_util::AliasDeclarationParser;
use jj_lib::dsl_util::AliasesMap;
use jj_lib::file_util;
use jj_lib::fileset;
use jj_lib::fileset::FilesetAliasesMap;
use jj_lib::fileset::FilesetDiagnostics;
use jj_lib::fileset::FilesetExpression;
use jj_lib::git;
Expand Down Expand Up @@ -338,6 +341,14 @@ impl CommandHelper {
load_template_aliases(ui, &self.data.layered_configs)
}

/// Loads fileset aliases from the configs.
///
/// For most commands that depend on a loaded repo, you should use
/// `WorkspaceCommandHelper::fileset_aliases_map()` instead.
fn load_fileset_aliases(&self, ui: &Ui) -> Result<FilesetAliasesMap, CommandError> {
load_fileset_aliases(ui, &self.data.layered_configs)
}

/// Parses template of the given language into evaluation tree.
///
/// This function also loads template aliases from the settings. Use
Expand Down Expand Up @@ -711,6 +722,7 @@ pub struct WorkspaceCommandEnvironment {
command: CommandHelper,
revset_aliases_map: RevsetAliasesMap,
template_aliases_map: TemplateAliasesMap,
fileset_aliases_map: FilesetAliasesMap,
path_converter: RepoPathUiConverter,
workspace_id: WorkspaceId,
immutable_heads_expression: Rc<UserRevsetExpression>,
Expand All @@ -724,6 +736,7 @@ impl WorkspaceCommandEnvironment {
let revset_aliases_map =
revset_util::load_revset_aliases(ui, &command.data.layered_configs)?;
let template_aliases_map = command.load_template_aliases(ui)?;
let fileset_aliases_map = command.load_fileset_aliases(ui)?;
let path_converter = RepoPathUiConverter::Fs {
cwd: command.cwd().to_owned(),
base: workspace.workspace_root().to_owned(),
Expand All @@ -732,6 +745,7 @@ impl WorkspaceCommandEnvironment {
command: command.clone(),
revset_aliases_map,
template_aliases_map,
fileset_aliases_map,
path_converter,
workspace_id: workspace.workspace_id().to_owned(),
immutable_heads_expression: RevsetExpression::root(),
Expand Down Expand Up @@ -1271,7 +1285,14 @@ to the current parents may contain changes from multiple commits.
let mut diagnostics = FilesetDiagnostics::new();
let expressions: Vec<_> = file_args
.iter()
.map(|arg| fileset::parse_maybe_bare(&mut diagnostics, arg, self.path_converter()))
.map(|arg| {
fileset::parse_maybe_bare(
&mut diagnostics,
arg,
self.path_converter(),
self.fileset_aliases_map(),
)
})
.try_collect()?;
print_parse_diagnostics(ui, "In fileset expression", &diagnostics)?;
Ok(FilesetExpression::union_all(expressions))
Expand All @@ -1287,6 +1308,7 @@ to the current parents may contain changes from multiple commits.
cwd: "".into(),
base: "".into(),
},
self.fileset_aliases_map(),
)?;
print_parse_diagnostics(ui, "In `snapshot.auto-track`", &diagnostics)?;
Ok(expression.to_matcher())
Expand Down Expand Up @@ -1562,6 +1584,10 @@ to the current parents may contain changes from multiple commits.
&self.env.template_aliases_map
}

pub fn fileset_aliases_map(&self) -> &FilesetAliasesMap {
&self.env.fileset_aliases_map
}

/// Parses template of the given language into evaluation tree.
///
/// `wrap_self` specifies the type of the top-level property, which should
Expand Down Expand Up @@ -2692,11 +2718,31 @@ fn load_template_aliases(
layered_configs: &LayeredConfigs,
) -> Result<TemplateAliasesMap, CommandError> {
const TABLE_KEY: &str = "template-aliases";
let mut aliases_map = TemplateAliasesMap::new();
load_aliases(ui, layered_configs, TABLE_KEY)
}

fn load_fileset_aliases(
ui: &Ui,
layered_configs: &LayeredConfigs,
) -> Result<FilesetAliasesMap, CommandError> {
const TABLE_KEY: &str = "fileset-aliases";
load_aliases(ui, layered_configs, TABLE_KEY)
}

fn load_aliases<P>(
ui: &Ui,
layered_configs: &LayeredConfigs,
table_key: &str,
) -> Result<AliasesMap<P, String>, CommandError>
where
P: Default + AliasDeclarationParser,
P::Error: ToString,
{
let mut aliases_map = AliasesMap::new();
// Load from all config layers in order. 'f(x)' in default layer should be
// overridden by 'f(a)' in user.
for (_, config) in layered_configs.sources() {
let table = if let Some(table) = config.get_table(TABLE_KEY).optional()? {
let table = if let Some(table) = config.get_table(table_key).optional()? {
table
} else {
continue;
Expand All @@ -2705,11 +2751,15 @@ fn load_template_aliases(
let r = value
.into_string()
.map_err(|e| e.to_string())
.and_then(|v| aliases_map.insert(&decl, v).map_err(|e| e.to_string()));
.and_then(|v| {
aliases_map
.insert(&decl, v)
.map_err(|e: P::Error| e.to_string())
});
if let Err(s) = r {
writeln!(
ui.warning_default(),
r#"Failed to load "{TABLE_KEY}.{decl}": {s}"#
r#"Failed to load "{table_key}.{decl}": {s}"#
)?;
}
}
Expand Down
3 changes: 3 additions & 0 deletions cli/src/command_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,9 @@ fn fileset_parse_error_hint(err: &FilesetParseError) -> Option<String> {
FilesetParseErrorKind::InvalidArguments { .. } | FilesetParseErrorKind::Expression(_) => {
find_source_parse_error_hint(&err)
}
FilesetParseErrorKind::InAliasExpansion(_)
| FilesetParseErrorKind::InParameterExpansion(_)
| FilesetParseErrorKind::RecursiveAlias(_) => None,
}
}

Expand Down
4 changes: 3 additions & 1 deletion cli/src/commands/debug/fileset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ pub fn cmd_debug_fileset(
) -> Result<(), CommandError> {
let workspace_command = command.workspace_helper(ui)?;
let path_converter = workspace_command.path_converter();
let aliases_map = workspace_command.fileset_aliases_map();

let mut diagnostics = FilesetDiagnostics::new();
let expression = fileset::parse_maybe_bare(&mut diagnostics, &args.path, path_converter)?;
let expression =
fileset::parse_maybe_bare(&mut diagnostics, &args.path, path_converter, aliases_map)?;
print_parse_diagnostics(ui, "In fileset expression", &diagnostics)?;
writeln!(ui.stdout(), "-- Parsed:")?;
writeln!(ui.stdout(), "{expression:#?}")?;
Expand Down
1 change: 1 addition & 0 deletions cli/src/commands/fix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@ fn get_tools_config(ui: &mut Ui, settings: &UserSettings) -> Result<ToolsConfig,
cwd: "".into(),
base: "".into(),
},
&Default::default(),
)
})
.try_collect()?,
Expand Down
5 changes: 3 additions & 2 deletions cli/src/commit_templater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -854,8 +854,9 @@ fn expect_fileset_literal(
) -> Result<FilesetExpression, TemplateParseError> {
template_parser::expect_string_literal_with(node, |text, span| {
let mut inner_diagnostics = FilesetDiagnostics::new();
let expression =
fileset::parse(&mut inner_diagnostics, text, path_converter).map_err(|err| {
let aliases_map = Default::default();
let expression = fileset::parse(&mut inner_diagnostics, text, path_converter, &aliases_map)
.map_err(|err| {
TemplateParseError::expression("In fileset expression", span).with_source(err)
})?;
diagnostics.extend_with(inner_diagnostics, |diag| {
Expand Down
2 changes: 2 additions & 0 deletions lib/src/fileset.pest
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,5 @@ program_or_bare_string = _{
| bare_string_pattern ~ EOI
| bare_string ~ EOI )
}

alias_declaration = _{ SOI ~ identifier ~ EOI }
56 changes: 52 additions & 4 deletions lib/src/fileset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@ use itertools::Itertools as _;
use once_cell::sync::Lazy;
use thiserror::Error;

use crate::dsl_util;
use crate::dsl_util::collect_similar;
use crate::dsl_util::AliasExpandError;
use crate::fileset_parser;
use crate::fileset_parser::BinaryOp;
use crate::fileset_parser::ExpressionKind;
use crate::fileset_parser::ExpressionNode;
pub use crate::fileset_parser::FilesetAliasesMap;
pub use crate::fileset_parser::FilesetDiagnostics;
pub use crate::fileset_parser::FilesetParseError;
pub use crate::fileset_parser::FilesetParseErrorKind;
Expand Down Expand Up @@ -465,6 +468,15 @@ fn resolve_expression(
ExpressionKind::FunctionCall(function) => {
resolve_function(diagnostics, path_converter, function)
}
ExpressionKind::AliasExpanded(id, subst) => {
let mut inner_diagnostics = FilesetDiagnostics::new();
let expression = resolve_expression(&mut inner_diagnostics, path_converter, subst)
.map_err(|e| e.within_alias_expansion(*id, node.span))?;
diagnostics.extend_with(inner_diagnostics, |diag| {
diag.within_alias_expansion(*id, node.span)
});
Ok(expression)
}
}
}

Expand All @@ -473,9 +485,11 @@ pub fn parse(
diagnostics: &mut FilesetDiagnostics,
text: &str,
path_converter: &RepoPathUiConverter,
aliases_map: &FilesetAliasesMap,
) -> FilesetParseResult<FilesetExpression> {
let node = fileset_parser::parse_program(text)?;
// TODO: add basic tree substitution pass to eliminate redundant expressions
let node = dsl_util::expand_aliases(node, aliases_map)?;
resolve_expression(diagnostics, path_converter, &node)
}

Expand All @@ -487,9 +501,11 @@ pub fn parse_maybe_bare(
diagnostics: &mut FilesetDiagnostics,
text: &str,
path_converter: &RepoPathUiConverter,
aliases_map: &FilesetAliasesMap,
) -> FilesetParseResult<FilesetExpression> {
let node = fileset_parser::parse_program_or_bare_string(text)?;
// TODO: add basic tree substitution pass to eliminate redundant expressions
let node = dsl_util::expand_aliases(node, aliases_map)?;
resolve_expression(diagnostics, path_converter, &node)
}

Expand Down Expand Up @@ -529,7 +545,15 @@ mod tests {
cwd: PathBuf::from("/ws/cur"),
base: PathBuf::from("/ws"),
};
let parse = |text| parse_maybe_bare(&mut FilesetDiagnostics::new(), text, &path_converter);
let aliases_map = Default::default();
let parse = |text| {
parse_maybe_bare(
&mut FilesetDiagnostics::new(),
text,
&path_converter,
&aliases_map,
)
};

// cwd-relative patterns
insta::assert_debug_snapshot!(
Expand Down Expand Up @@ -574,7 +598,15 @@ mod tests {
cwd: PathBuf::from("/ws/cur*"),
base: PathBuf::from("/ws"),
};
let parse = |text| parse_maybe_bare(&mut FilesetDiagnostics::new(), text, &path_converter);
let aliases_map = Default::default();
let parse = |text| {
parse_maybe_bare(
&mut FilesetDiagnostics::new(),
text,
&path_converter,
&aliases_map,
)
};

// cwd-relative, without meta characters
insta::assert_debug_snapshot!(
Expand Down Expand Up @@ -747,7 +779,15 @@ mod tests {
cwd: PathBuf::from("/ws/cur"),
base: PathBuf::from("/ws"),
};
let parse = |text| parse_maybe_bare(&mut FilesetDiagnostics::new(), text, &path_converter);
let aliases_map = Default::default();
let parse = |text| {
parse_maybe_bare(
&mut FilesetDiagnostics::new(),
text,
&path_converter,
&aliases_map,
)
};

insta::assert_debug_snapshot!(parse("all()").unwrap(), @"All");
insta::assert_debug_snapshot!(parse("none()").unwrap(), @"None");
Expand Down Expand Up @@ -775,7 +815,15 @@ mod tests {
cwd: PathBuf::from("/ws/cur"),
base: PathBuf::from("/ws"),
};
let parse = |text| parse_maybe_bare(&mut FilesetDiagnostics::new(), text, &path_converter);
let aliases_map = Default::default();
let parse = |text| {
parse_maybe_bare(
&mut FilesetDiagnostics::new(),
text,
&path_converter,
&aliases_map,
)
};

insta::assert_debug_snapshot!(parse("~x").unwrap(), @r###"
Difference(
Expand Down
Loading

0 comments on commit 6b1e2d3

Please sign in to comment.