mirror of
https://github.com/martinvonz/jj.git
synced 2025-01-15 08:53:16 +00:00
revset: add table of symbol aliases and pass around parse functions
The CLI will load aliases from config, insert them one by one, and warn if declaration part is invalid. That's why RevsetAliasesMap is a public struct and needs to be instantiated by the caller.
This commit is contained in:
parent
f0b1221749
commit
7632466cc0
5 changed files with 104 additions and 11 deletions
|
@ -68,3 +68,7 @@ expression = {
|
|||
}
|
||||
|
||||
program = _{ SOI ~ expression ~ EOI }
|
||||
|
||||
alias_declaration = _{
|
||||
SOI ~ identifier ~ EOI
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
use std::borrow::Borrow;
|
||||
use std::cmp::{Ordering, Reverse};
|
||||
use std::collections::HashSet;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::iter::Peekable;
|
||||
use std::ops::Range;
|
||||
use std::path::Path;
|
||||
|
@ -474,8 +474,76 @@ impl RevsetExpression {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct RevsetAliasesMap {
|
||||
symbol_aliases: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl RevsetAliasesMap {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Adds new substitution rule `decl = defn`.
|
||||
///
|
||||
/// Returns error if `decl` is invalid. The `defn` part isn't checked. A bad
|
||||
/// `defn` will be reported when the alias is substituted.
|
||||
pub fn insert(
|
||||
&mut self,
|
||||
decl: impl AsRef<str>,
|
||||
defn: impl Into<String>,
|
||||
) -> Result<(), RevsetParseError> {
|
||||
match RevsetAliasDeclaration::parse(decl.as_ref())? {
|
||||
RevsetAliasDeclaration::Symbol(name) => {
|
||||
self.symbol_aliases.insert(name, defn.into());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_symbol<'a>(&'a self, name: &str) -> Option<(RevsetAliasId<'a>, &'a str)> {
|
||||
self.symbol_aliases
|
||||
.get_key_value(name)
|
||||
.map(|(name, defn)| (RevsetAliasId::Symbol(name), defn.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Parsed declaration part of alias rule.
|
||||
#[derive(Clone, Debug)]
|
||||
enum RevsetAliasDeclaration {
|
||||
Symbol(String),
|
||||
// TODO: Function(String, Vec<String>)
|
||||
}
|
||||
|
||||
impl RevsetAliasDeclaration {
|
||||
fn parse(source: &str) -> Result<Self, RevsetParseError> {
|
||||
let mut pairs = RevsetParser::parse(Rule::alias_declaration, source)?;
|
||||
let first = pairs.next().unwrap();
|
||||
match first.as_rule() {
|
||||
Rule::identifier => Ok(RevsetAliasDeclaration::Symbol(first.as_str().to_owned())),
|
||||
r => panic!("unxpected alias declaration rule {r:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Borrowed reference to identify alias expression.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
enum RevsetAliasId<'a> {
|
||||
Symbol(&'a str),
|
||||
// TODO: Function(&'a str)
|
||||
}
|
||||
|
||||
impl fmt::Display for RevsetAliasId<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
RevsetAliasId::Symbol(name) => write!(f, "{name}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct ParseState<'a> {
|
||||
aliases_map: &'a RevsetAliasesMap,
|
||||
workspace_ctx: Option<&'a RevsetWorkspaceContext<'a>>,
|
||||
}
|
||||
|
||||
|
@ -791,11 +859,15 @@ fn parse_function_argument_to_string(
|
|||
|
||||
pub fn parse(
|
||||
revset_str: &str,
|
||||
aliases_map: &RevsetAliasesMap,
|
||||
workspace_ctx: Option<&RevsetWorkspaceContext>,
|
||||
) -> Result<Rc<RevsetExpression>, RevsetParseError> {
|
||||
let mut pairs = RevsetParser::parse(Rule::program, revset_str)?;
|
||||
let first = pairs.next().unwrap();
|
||||
let state = ParseState { workspace_ctx };
|
||||
let state = ParseState {
|
||||
aliases_map,
|
||||
workspace_ctx,
|
||||
};
|
||||
parse_expression_rule(first.into_inner(), state)
|
||||
}
|
||||
|
||||
|
@ -1624,6 +1696,17 @@ mod tests {
|
|||
use super::*;
|
||||
|
||||
fn parse(revset_str: &str) -> Result<Rc<RevsetExpression>, RevsetParseErrorKind> {
|
||||
parse_with_aliases(revset_str, [] as [(&str, &str); 0])
|
||||
}
|
||||
|
||||
fn parse_with_aliases(
|
||||
revset_str: &str,
|
||||
aliases: impl IntoIterator<Item = (impl AsRef<str>, impl Into<String>)>,
|
||||
) -> Result<Rc<RevsetExpression>, RevsetParseErrorKind> {
|
||||
let mut aliases_map = RevsetAliasesMap::new();
|
||||
for (decl, defn) in aliases {
|
||||
aliases_map.insert(decl, defn).unwrap();
|
||||
}
|
||||
// Set up pseudo context to resolve file(path)
|
||||
let workspace_ctx = RevsetWorkspaceContext {
|
||||
cwd: Path::new("/"),
|
||||
|
@ -1631,7 +1714,7 @@ mod tests {
|
|||
workspace_root: Path::new("/"),
|
||||
};
|
||||
// Map error to comparable object
|
||||
super::parse(revset_str, Some(&workspace_ctx)).map_err(|e| e.kind)
|
||||
super::parse(revset_str, &aliases_map, Some(&workspace_ctx)).map_err(|e| e.kind)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -22,7 +22,8 @@ use jujutsu_lib::op_store::{RefTarget, WorkspaceId};
|
|||
use jujutsu_lib::repo::RepoRef;
|
||||
use jujutsu_lib::repo_path::RepoPath;
|
||||
use jujutsu_lib::revset::{
|
||||
self, optimize, parse, resolve_symbol, RevsetError, RevsetExpression, RevsetWorkspaceContext,
|
||||
self, optimize, parse, resolve_symbol, RevsetAliasesMap, RevsetError, RevsetExpression,
|
||||
RevsetWorkspaceContext,
|
||||
};
|
||||
use jujutsu_lib::workspace::Workspace;
|
||||
use test_case::test_case;
|
||||
|
@ -135,7 +136,7 @@ fn test_resolve_symbol_commit_id() {
|
|||
// Test present() suppresses only NoSuchRevision error
|
||||
assert_eq!(resolve_commit_ids(repo_ref, "present(foo)"), []);
|
||||
assert_eq!(
|
||||
optimize(parse("present(04)", None).unwrap())
|
||||
optimize(parse("present(04)", &RevsetAliasesMap::new(), None).unwrap())
|
||||
.evaluate(repo_ref, None)
|
||||
.map(|_| ()),
|
||||
Err(RevsetError::AmbiguousCommitIdPrefix("04".to_string()))
|
||||
|
@ -443,7 +444,7 @@ fn test_resolve_symbol_git_refs() {
|
|||
}
|
||||
|
||||
fn resolve_commit_ids(repo: RepoRef, revset_str: &str) -> Vec<CommitId> {
|
||||
let expression = optimize(parse(revset_str, None).unwrap());
|
||||
let expression = optimize(parse(revset_str, &RevsetAliasesMap::new(), None).unwrap());
|
||||
expression
|
||||
.evaluate(repo, None)
|
||||
.unwrap()
|
||||
|
@ -463,7 +464,8 @@ fn resolve_commit_ids_in_workspace(
|
|||
workspace_id: workspace.workspace_id(),
|
||||
workspace_root: workspace.workspace_root(),
|
||||
};
|
||||
let expression = optimize(parse(revset_str, Some(&workspace_ctx)).unwrap());
|
||||
let expression =
|
||||
optimize(parse(revset_str, &RevsetAliasesMap::new(), Some(&workspace_ctx)).unwrap());
|
||||
expression
|
||||
.evaluate(repo, Some(&workspace_ctx))
|
||||
.unwrap()
|
||||
|
|
|
@ -36,7 +36,8 @@ use jujutsu_lib::operation::Operation;
|
|||
use jujutsu_lib::repo::{BackendFactories, MutableRepo, ReadonlyRepo, RepoRef, RewriteRootCommit};
|
||||
use jujutsu_lib::repo_path::{FsPathParseError, RepoPath};
|
||||
use jujutsu_lib::revset::{
|
||||
Revset, RevsetError, RevsetExpression, RevsetParseError, RevsetWorkspaceContext,
|
||||
Revset, RevsetAliasesMap, RevsetError, RevsetExpression, RevsetParseError,
|
||||
RevsetWorkspaceContext,
|
||||
};
|
||||
use jujutsu_lib::settings::UserSettings;
|
||||
use jujutsu_lib::transaction::Transaction;
|
||||
|
@ -606,7 +607,8 @@ impl WorkspaceCommandHelper {
|
|||
&self,
|
||||
revision_str: &str,
|
||||
) -> Result<Rc<RevsetExpression>, RevsetParseError> {
|
||||
let expression = revset::parse(revision_str, Some(&self.revset_context()))?;
|
||||
let aliases_map = RevsetAliasesMap::new(); // TODO: load from settings
|
||||
let expression = revset::parse(revision_str, &aliases_map, Some(&self.revset_context()))?;
|
||||
Ok(revset::optimize(expression))
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ use jujutsu_lib::operation::Operation;
|
|||
use jujutsu_lib::refs::{classify_branch_push_action, BranchPushAction, BranchPushUpdate};
|
||||
use jujutsu_lib::repo::{ReadonlyRepo, RepoRef};
|
||||
use jujutsu_lib::repo_path::RepoPath;
|
||||
use jujutsu_lib::revset::RevsetExpression;
|
||||
use jujutsu_lib::revset::{RevsetAliasesMap, RevsetExpression};
|
||||
use jujutsu_lib::revset_graph_iterator::{RevsetGraphEdge, RevsetGraphEdgeType};
|
||||
use jujutsu_lib::rewrite::{back_out_commit, merge_commit_trees, rebase_commit, DescendantRebaser};
|
||||
use jujutsu_lib::settings::UserSettings;
|
||||
|
@ -2164,7 +2164,9 @@ fn cmd_log(ui: &mut Ui, command: &CommandHelper, args: &LogArgs) -> Result<(), C
|
|||
often not useful because all non-empty commits touch '.'. If you meant to show \
|
||||
the working copy commit, pass -r '@' instead.\n"
|
||||
))?;
|
||||
} else if revset.is_empty() && revset::parse(only_path, None).is_ok() {
|
||||
} else if revset.is_empty()
|
||||
&& revset::parse(only_path, &RevsetAliasesMap::new(), None).is_ok()
|
||||
{
|
||||
ui.write_warn(&format!(
|
||||
"warning: The argument {only_path:?} is being interpreted as a path. To specify a \
|
||||
revset, pass -r {only_path:?} instead.\n"
|
||||
|
|
Loading…
Reference in a new issue