forked from mirrors/jj
cli: load revset aliases from config file
Aliases are loaded at WorkspaceCommandHelper::new() as it's easier to warn invalid declarations there. Not all commands use revsets, but many do, so I think it's okay to always pay the loading cost. Parsing the declaration part (i.e. a symbol) should be fast anyway. The nested error message isn't super readable, but seems good enough. Config syntax to bikeshed: - naming: [revset-alias] vs [revset-aliases] ? - function alias will need quotes: 'f(x)' = 'x'
This commit is contained in:
parent
11ee2f22c4
commit
8b00a64ab2
4 changed files with 136 additions and 5 deletions
|
@ -45,6 +45,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
* The new revset function `empty()` finds commits modifying no files.
|
||||
|
||||
* Added support for revset aliases. New symbols can be configured by
|
||||
`revset-aliases.<name> = <expression>`.
|
||||
|
||||
* It is now possible to specify configuration options on the command line
|
||||
with the new `--config-toml` global option.
|
||||
|
||||
|
|
|
@ -115,6 +115,18 @@ revsets (expressions) as arguments.
|
|||
in `x` doesn't exist (e.g. is an unknown branch name.)
|
||||
|
||||
|
||||
## Aliases
|
||||
|
||||
New symbols can be defined in the config file, by using any combination
|
||||
of the predefined symbols/functions and other aliases.
|
||||
|
||||
For example:
|
||||
```toml
|
||||
[revset-aliases]
|
||||
'mine' = 'author(martinvonz)'
|
||||
```
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
Show the parent(s) of the working-copy commit (like `git log -1 HEAD`):
|
||||
|
|
|
@ -307,7 +307,7 @@ jj init --git-repo=.",
|
|||
|
||||
pub fn for_loaded_repo(
|
||||
&self,
|
||||
ui: &Ui,
|
||||
ui: &mut Ui,
|
||||
workspace: Workspace,
|
||||
repo: Arc<ReadonlyRepo>,
|
||||
) -> Result<WorkspaceCommandHelper, CommandError> {
|
||||
|
@ -330,13 +330,14 @@ pub struct WorkspaceCommandHelper {
|
|||
settings: UserSettings,
|
||||
workspace: Workspace,
|
||||
repo: Arc<ReadonlyRepo>,
|
||||
revset_aliases_map: RevsetAliasesMap,
|
||||
may_update_working_copy: bool,
|
||||
working_copy_shared_with_git: bool,
|
||||
}
|
||||
|
||||
impl WorkspaceCommandHelper {
|
||||
pub fn new(
|
||||
ui: &Ui,
|
||||
ui: &mut Ui,
|
||||
workspace: Workspace,
|
||||
string_args: Vec<String>,
|
||||
global_args: &GlobalArgs,
|
||||
|
@ -360,6 +361,7 @@ impl WorkspaceCommandHelper {
|
|||
settings: ui.settings().clone(),
|
||||
workspace,
|
||||
repo,
|
||||
revset_aliases_map: load_revset_aliases(ui)?,
|
||||
may_update_working_copy,
|
||||
working_copy_shared_with_git,
|
||||
})
|
||||
|
@ -609,8 +611,11 @@ impl WorkspaceCommandHelper {
|
|||
&self,
|
||||
revision_str: &str,
|
||||
) -> Result<Rc<RevsetExpression>, RevsetParseError> {
|
||||
let aliases_map = RevsetAliasesMap::new(); // TODO: load from settings
|
||||
let expression = revset::parse(revision_str, &aliases_map, Some(&self.revset_context()))?;
|
||||
let expression = revset::parse(
|
||||
revision_str,
|
||||
&self.revset_aliases_map,
|
||||
Some(&self.revset_context()),
|
||||
)?;
|
||||
Ok(revset::optimize(expression))
|
||||
}
|
||||
|
||||
|
@ -1024,6 +1029,23 @@ fn resolve_single_op_from_store(
|
|||
}
|
||||
}
|
||||
|
||||
fn load_revset_aliases(ui: &mut Ui) -> Result<RevsetAliasesMap, CommandError> {
|
||||
const TABLE_KEY: &str = "revset-aliases";
|
||||
let mut aliases_map = RevsetAliasesMap::new();
|
||||
if let Ok(table) = ui.settings().config().get_table(TABLE_KEY) {
|
||||
for (decl, value) in table.into_iter().sorted_by(|a, b| a.0.cmp(&b.0)) {
|
||||
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()));
|
||||
if let Err(s) = r {
|
||||
ui.write_warn(format!("Failed to load \"{TABLE_KEY}.{decl}\": {s}\n"))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(aliases_map)
|
||||
}
|
||||
|
||||
pub fn resolve_base_revs(
|
||||
workspace_command: &WorkspaceCommandHelper,
|
||||
revisions: &[String],
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use common::TestEnvironment;
|
||||
use common::{get_stderr_string, get_stdout_string, TestEnvironment};
|
||||
|
||||
pub mod common;
|
||||
|
||||
|
@ -119,3 +119,97 @@ fn test_bad_function_call() {
|
|||
= Revset function "whatever" doesn't exist
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_alias() {
|
||||
let test_env = TestEnvironment::default();
|
||||
test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]);
|
||||
let repo_path = test_env.env_root().join("repo");
|
||||
|
||||
test_env.add_config(
|
||||
br###"
|
||||
[revset-aliases]
|
||||
'my-root' = 'root'
|
||||
'syntax-error' = 'whatever &'
|
||||
'recurse' = 'recurse1'
|
||||
'recurse1' = 'recurse'
|
||||
"###,
|
||||
);
|
||||
|
||||
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-r", "my-root"]);
|
||||
insta::assert_snapshot!(stdout, @r###"
|
||||
o 000000000000 1970-01-01 00:00:00.000 +00:00 000000000000
|
||||
(no description set)
|
||||
"###);
|
||||
|
||||
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "root & syntax-error"]);
|
||||
insta::assert_snapshot!(stderr, @r###"
|
||||
Error: Failed to parse revset: --> 1:8
|
||||
|
|
||||
1 | root & syntax-error
|
||||
| ^----------^
|
||||
|
|
||||
= Alias "syntax-error" cannot be expanded
|
||||
--> 1:11
|
||||
|
|
||||
1 | whatever &
|
||||
| ^---
|
||||
|
|
||||
= expected dag_range_pre_op, range_pre_op, or primary
|
||||
"###);
|
||||
|
||||
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "root & recurse"]);
|
||||
insta::assert_snapshot!(stderr, @r###"
|
||||
Error: Failed to parse revset: --> 1:8
|
||||
|
|
||||
1 | root & recurse
|
||||
| ^-----^
|
||||
|
|
||||
= Alias "recurse" cannot be expanded
|
||||
--> 1:1
|
||||
|
|
||||
1 | recurse1
|
||||
| ^------^
|
||||
|
|
||||
= Alias "recurse1" cannot be expanded
|
||||
--> 1:1
|
||||
|
|
||||
1 | recurse
|
||||
| ^-----^
|
||||
|
|
||||
= Alias "recurse" expanded recursively
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bad_alias_decl() {
|
||||
let test_env = TestEnvironment::default();
|
||||
test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]);
|
||||
let repo_path = test_env.env_root().join("repo");
|
||||
|
||||
test_env.add_config(
|
||||
br###"
|
||||
[revset-aliases]
|
||||
'my-root' = 'root'
|
||||
'"bad"' = 'root'
|
||||
"###,
|
||||
);
|
||||
|
||||
// Invalid declaration should be warned and ignored.
|
||||
let assert = test_env
|
||||
.jj_cmd(&repo_path, &["log", "-r", "my-root"])
|
||||
.assert()
|
||||
.success();
|
||||
insta::assert_snapshot!(get_stdout_string(&assert), @r###"
|
||||
o 000000000000 1970-01-01 00:00:00.000 +00:00 000000000000
|
||||
(no description set)
|
||||
"###);
|
||||
insta::assert_snapshot!(get_stderr_string(&assert), @r###"
|
||||
Failed to load "revset-aliases."bad"": --> 1:1
|
||||
|
|
||||
1 | "bad"
|
||||
| ^---
|
||||
|
|
||||
= expected identifier
|
||||
"###);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue