mirror of
https://github.com/encounter/objdiff.git
synced 2025-08-17 17:31:31 +00:00
Add "ignore_patterns" option to config
This allows explicitly ignoring changes to certain files or directories, even if the changed file ends up matching `watch_patterns`. The default value, `build/**/*` will ensure that changes in the build directory will not trigger a duplicate rebuild. Resolves #143 Resolves #215
This commit is contained in:
parent
813c8aa539
commit
52c138bf06
@ -106,6 +106,9 @@ file as well. You can then add `objdiff.json` to your `.gitignore` to prevent it
|
|||||||
"*.txt",
|
"*.txt",
|
||||||
"*.json"
|
"*.json"
|
||||||
],
|
],
|
||||||
|
"ignore_patterns": [
|
||||||
|
"build/**/*"
|
||||||
|
],
|
||||||
"units": [
|
"units": [
|
||||||
{
|
{
|
||||||
"name": "main/MetroTRK/mslsupp",
|
"name": "main/MetroTRK/mslsupp",
|
||||||
@ -141,6 +144,10 @@ It's unlikely you'll want to disable this, unless you're using an external tool
|
|||||||
If any of these files change, objdiff will automatically rebuild the objects and re-compare them.
|
If any of these files change, objdiff will automatically rebuild the objects and re-compare them.
|
||||||
If not specified, objdiff will use the default patterns listed above.
|
If not specified, objdiff will use the default patterns listed above.
|
||||||
|
|
||||||
|
`ignore_patterns` _(optional)_: A list of glob patterns to explicitly ignore when watching for changes.
|
||||||
|
([Supported syntax](https://docs.rs/globset/latest/globset/#syntax))
|
||||||
|
If not specified, objdiff will use the default patterns listed above.
|
||||||
|
|
||||||
`units` _(optional)_: If specified, objdiff will display a list of objects in the sidebar for easy navigation.
|
`units` _(optional)_: If specified, objdiff will display a list of objects in the sidebar for easy navigation.
|
||||||
|
|
||||||
> `name` _(optional)_: The name of the object in the UI. If not specified, the object's `path` will be used.
|
> `name` _(optional)_: The name of the object in the UI. If not specified, the object's `path` will be used.
|
||||||
|
@ -74,6 +74,16 @@
|
|||||||
"*.json"
|
"*.json"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"ignore_patterns": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "List of glob patterns to explicitly ignore when watching for changes.\nFiles matching these patterns will not trigger a rebuild.\nSupported syntax: https://docs.rs/globset/latest/globset/#syntax",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"default": [
|
||||||
|
"build/**/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
"objects": {
|
"objects": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"description": "Use units instead.",
|
"description": "Use units instead.",
|
||||||
|
@ -342,10 +342,12 @@ fn run_interactive(
|
|||||||
};
|
};
|
||||||
if let (Some(project_dir), Some(project_config)) = (&state.project_dir, &state.project_config) {
|
if let (Some(project_dir), Some(project_config)) = (&state.project_dir, &state.project_config) {
|
||||||
let watch_patterns = project_config.build_watch_patterns()?;
|
let watch_patterns = project_config.build_watch_patterns()?;
|
||||||
|
let ignore_patterns = project_config.build_ignore_patterns()?;
|
||||||
state.watcher = Some(create_watcher(
|
state.watcher = Some(create_watcher(
|
||||||
state.modified.clone(),
|
state.modified.clone(),
|
||||||
project_dir.as_ref(),
|
project_dir.as_ref(),
|
||||||
build_globset(&watch_patterns)?,
|
build_globset(&watch_patterns)?,
|
||||||
|
build_globset(&ignore_patterns)?,
|
||||||
Waker::from(state.waker.clone()),
|
Waker::from(state.waker.clone()),
|
||||||
)?);
|
)?);
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ pub fn create_watcher(
|
|||||||
modified: Arc<AtomicBool>,
|
modified: Arc<AtomicBool>,
|
||||||
project_dir: &Path,
|
project_dir: &Path,
|
||||||
patterns: GlobSet,
|
patterns: GlobSet,
|
||||||
|
ignore_patterns: GlobSet,
|
||||||
waker: Waker,
|
waker: Waker,
|
||||||
) -> notify::Result<Watcher> {
|
) -> notify::Result<Watcher> {
|
||||||
let base_dir = fs::canonicalize(project_dir)?;
|
let base_dir = fs::canonicalize(project_dir)?;
|
||||||
@ -54,8 +55,8 @@ pub fn create_watcher(
|
|||||||
let Ok(path) = path.strip_prefix(&base_dir_clone) else {
|
let Ok(path) = path.strip_prefix(&base_dir_clone) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if patterns.is_match(path) {
|
if patterns.is_match(path) && !ignore_patterns.is_match(path) {
|
||||||
// log::info!("File modified: {}", path.display());
|
log::info!("File modified: {}", path.display());
|
||||||
any_match = true;
|
any_match = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,8 @@ pub struct ProjectConfig {
|
|||||||
pub build_target: Option<bool>,
|
pub build_target: Option<bool>,
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
pub watch_patterns: Option<Vec<String>>,
|
pub watch_patterns: Option<Vec<String>>,
|
||||||
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
|
pub ignore_patterns: Option<Vec<String>>,
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
serde(alias = "objects", skip_serializing_if = "Option::is_none")
|
serde(alias = "objects", skip_serializing_if = "Option::is_none")
|
||||||
@ -66,7 +68,18 @@ impl ProjectConfig {
|
|||||||
.map(|s| Glob::new(s))
|
.map(|s| Glob::new(s))
|
||||||
.collect::<Result<Vec<Glob>, globset::Error>>()?
|
.collect::<Result<Vec<Glob>, globset::Error>>()?
|
||||||
} else {
|
} else {
|
||||||
DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect()
|
default_watch_patterns()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_ignore_patterns(&self) -> Result<Vec<Glob>, globset::Error> {
|
||||||
|
Ok(if let Some(ignore_patterns) = &self.ignore_patterns {
|
||||||
|
ignore_patterns
|
||||||
|
.iter()
|
||||||
|
.map(|s| Glob::new(s))
|
||||||
|
.collect::<Result<Vec<Glob>, globset::Error>>()?
|
||||||
|
} else {
|
||||||
|
default_ignore_patterns()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -195,10 +208,16 @@ pub const DEFAULT_WATCH_PATTERNS: &[&str] = &[
|
|||||||
"*.inc", "*.py", "*.yml", "*.txt", "*.json",
|
"*.inc", "*.py", "*.yml", "*.txt", "*.json",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
pub const DEFAULT_IGNORE_PATTERNS: &[&str] = &["build/**/*"];
|
||||||
|
|
||||||
pub fn default_watch_patterns() -> Vec<Glob> {
|
pub fn default_watch_patterns() -> Vec<Glob> {
|
||||||
DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect()
|
DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn default_ignore_patterns() -> Vec<Glob> {
|
||||||
|
DEFAULT_IGNORE_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[derive(Clone, Eq, PartialEq)]
|
#[derive(Clone, Eq, PartialEq)]
|
||||||
pub struct ProjectConfigInfo {
|
pub struct ProjectConfigInfo {
|
||||||
|
@ -16,8 +16,8 @@ use globset::Glob;
|
|||||||
use objdiff_core::{
|
use objdiff_core::{
|
||||||
build::watcher::{Watcher, create_watcher},
|
build::watcher::{Watcher, create_watcher},
|
||||||
config::{
|
config::{
|
||||||
DEFAULT_WATCH_PATTERNS, ProjectConfig, ProjectConfigInfo, ProjectObject, ScratchConfig,
|
ProjectConfig, ProjectConfigInfo, ProjectObject, ScratchConfig, build_globset,
|
||||||
build_globset, default_watch_patterns, path::platform_path_serde_option,
|
default_ignore_patterns, default_watch_patterns, path::platform_path_serde_option,
|
||||||
save_project_config,
|
save_project_config,
|
||||||
},
|
},
|
||||||
diff::DiffObjConfig,
|
diff::DiffObjConfig,
|
||||||
@ -219,6 +219,8 @@ pub struct AppConfig {
|
|||||||
#[serde(default = "default_watch_patterns")]
|
#[serde(default = "default_watch_patterns")]
|
||||||
pub watch_patterns: Vec<Glob>,
|
pub watch_patterns: Vec<Glob>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
pub ignore_patterns: Vec<Glob>,
|
||||||
|
#[serde(default)]
|
||||||
pub recent_projects: Vec<String>,
|
pub recent_projects: Vec<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub diff_obj_config: DiffObjConfig,
|
pub diff_obj_config: DiffObjConfig,
|
||||||
@ -239,7 +241,8 @@ impl Default for AppConfig {
|
|||||||
build_target: false,
|
build_target: false,
|
||||||
rebuild_on_changes: true,
|
rebuild_on_changes: true,
|
||||||
auto_update_check: true,
|
auto_update_check: true,
|
||||||
watch_patterns: DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect(),
|
watch_patterns: default_watch_patterns(),
|
||||||
|
ignore_patterns: default_ignore_patterns(),
|
||||||
recent_projects: vec![],
|
recent_projects: vec![],
|
||||||
diff_obj_config: Default::default(),
|
diff_obj_config: Default::default(),
|
||||||
}
|
}
|
||||||
@ -560,11 +563,17 @@ impl App {
|
|||||||
if let Some(project_dir) = &state.config.project_dir {
|
if let Some(project_dir) = &state.config.project_dir {
|
||||||
match build_globset(&state.config.watch_patterns)
|
match build_globset(&state.config.watch_patterns)
|
||||||
.map_err(anyhow::Error::new)
|
.map_err(anyhow::Error::new)
|
||||||
.and_then(|globset| {
|
.and_then(|patterns| {
|
||||||
|
build_globset(&state.config.ignore_patterns)
|
||||||
|
.map(|ignore_patterns| (patterns, ignore_patterns))
|
||||||
|
.map_err(anyhow::Error::new)
|
||||||
|
})
|
||||||
|
.and_then(|(patterns, ignore_patterns)| {
|
||||||
create_watcher(
|
create_watcher(
|
||||||
self.modified.clone(),
|
self.modified.clone(),
|
||||||
project_dir.as_ref(),
|
project_dir.as_ref(),
|
||||||
globset,
|
patterns,
|
||||||
|
ignore_patterns,
|
||||||
egui_waker(ctx),
|
egui_waker(ctx),
|
||||||
)
|
)
|
||||||
.map_err(anyhow::Error::new)
|
.map_err(anyhow::Error::new)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use globset::Glob;
|
use globset::Glob;
|
||||||
use objdiff_core::config::{DEFAULT_WATCH_PATTERNS, try_project_config};
|
use objdiff_core::config::{default_ignore_patterns, default_watch_patterns, try_project_config};
|
||||||
use typed_path::{Utf8UnixComponent, Utf8UnixPath};
|
use typed_path::{Utf8UnixComponent, Utf8UnixPath};
|
||||||
|
|
||||||
use crate::app::{AppState, ObjectConfig};
|
use crate::app::{AppState, ObjectConfig};
|
||||||
@ -96,8 +96,15 @@ pub fn load_project_config(state: &mut AppState) -> Result<()> {
|
|||||||
.map(|s| Glob::new(s))
|
.map(|s| Glob::new(s))
|
||||||
.collect::<Result<Vec<Glob>, globset::Error>>()?;
|
.collect::<Result<Vec<Glob>, globset::Error>>()?;
|
||||||
} else {
|
} else {
|
||||||
state.config.watch_patterns =
|
state.config.watch_patterns = default_watch_patterns();
|
||||||
DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect();
|
}
|
||||||
|
if let Some(ignore_patterns) = &project_config.ignore_patterns {
|
||||||
|
state.config.ignore_patterns = ignore_patterns
|
||||||
|
.iter()
|
||||||
|
.map(|s| Glob::new(s))
|
||||||
|
.collect::<Result<Vec<Glob>, globset::Error>>()?;
|
||||||
|
} else {
|
||||||
|
state.config.ignore_patterns = default_ignore_patterns();
|
||||||
}
|
}
|
||||||
state.watcher_change = true;
|
state.watcher_change = true;
|
||||||
state.objects = project_config
|
state.objects = project_config
|
||||||
|
@ -10,7 +10,7 @@ use egui::{
|
|||||||
};
|
};
|
||||||
use globset::Glob;
|
use globset::Glob;
|
||||||
use objdiff_core::{
|
use objdiff_core::{
|
||||||
config::{DEFAULT_WATCH_PATTERNS, path::check_path_buf},
|
config::{default_ignore_patterns, default_watch_patterns, path::check_path_buf},
|
||||||
diff::{
|
diff::{
|
||||||
CONFIG_GROUPS, ConfigEnum, ConfigEnumVariantInfo, ConfigPropertyId, ConfigPropertyKind,
|
CONFIG_GROUPS, ConfigEnum, ConfigEnumVariantInfo, ConfigPropertyId, ConfigPropertyKind,
|
||||||
ConfigPropertyValue,
|
ConfigPropertyValue,
|
||||||
@ -41,6 +41,7 @@ pub struct ConfigViewState {
|
|||||||
pub build_running: bool,
|
pub build_running: bool,
|
||||||
pub queue_build: bool,
|
pub queue_build: bool,
|
||||||
pub watch_pattern_text: String,
|
pub watch_pattern_text: String,
|
||||||
|
pub ignore_pattern_text: String,
|
||||||
pub object_search: String,
|
pub object_search: String,
|
||||||
pub filter_diffable: bool,
|
pub filter_diffable: bool,
|
||||||
pub filter_incomplete: bool,
|
pub filter_incomplete: bool,
|
||||||
@ -790,20 +791,49 @@ fn split_obj_config_ui(
|
|||||||
state.watcher_change = true;
|
state.watcher_change = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
state.watcher_change |= patterns_ui(
|
||||||
|
ui,
|
||||||
|
"File patterns",
|
||||||
|
&mut state.config.watch_patterns,
|
||||||
|
&mut config_state.watch_pattern_text,
|
||||||
|
appearance,
|
||||||
|
state.project_config_info.is_some(),
|
||||||
|
default_watch_patterns,
|
||||||
|
);
|
||||||
|
state.watcher_change |= patterns_ui(
|
||||||
|
ui,
|
||||||
|
"Ignore patterns",
|
||||||
|
&mut state.config.ignore_patterns,
|
||||||
|
&mut config_state.ignore_pattern_text,
|
||||||
|
appearance,
|
||||||
|
state.project_config_info.is_some(),
|
||||||
|
default_ignore_patterns,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn patterns_ui(
|
||||||
|
ui: &mut egui::Ui,
|
||||||
|
text: &str,
|
||||||
|
patterns: &mut Vec<Glob>,
|
||||||
|
pattern_text: &mut String,
|
||||||
|
appearance: &Appearance,
|
||||||
|
has_project_config: bool,
|
||||||
|
on_reset: impl FnOnce() -> Vec<Glob>,
|
||||||
|
) -> bool {
|
||||||
|
let mut change = false;
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label(RichText::new("File patterns").color(appearance.text_color));
|
ui.label(RichText::new(text).color(appearance.text_color));
|
||||||
if ui
|
if ui
|
||||||
.add_enabled(state.project_config_info.is_none(), egui::Button::new("Reset"))
|
.add_enabled(!has_project_config, egui::Button::new("Reset"))
|
||||||
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
|
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
state.config.watch_patterns =
|
*patterns = on_reset();
|
||||||
DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect();
|
change = true;
|
||||||
state.watcher_change = true;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let mut remove_at: Option<usize> = None;
|
let mut remove_at: Option<usize> = None;
|
||||||
for (idx, glob) in state.config.watch_patterns.iter().enumerate() {
|
for (idx, glob) in patterns.iter().enumerate() {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label(
|
ui.label(
|
||||||
RichText::new(glob.to_string())
|
RichText::new(glob.to_string())
|
||||||
@ -811,7 +841,7 @@ fn split_obj_config_ui(
|
|||||||
.family(FontFamily::Monospace),
|
.family(FontFamily::Monospace),
|
||||||
);
|
);
|
||||||
if ui
|
if ui
|
||||||
.add_enabled(state.project_config_info.is_none(), egui::Button::new("-").small())
|
.add_enabled(!has_project_config, egui::Button::new("-").small())
|
||||||
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
|
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
@ -820,26 +850,27 @@ fn split_obj_config_ui(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if let Some(idx) = remove_at {
|
if let Some(idx) = remove_at {
|
||||||
state.config.watch_patterns.remove(idx);
|
patterns.remove(idx);
|
||||||
state.watcher_change = true;
|
change = true;
|
||||||
}
|
}
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.add_enabled(
|
ui.add_enabled(
|
||||||
state.project_config_info.is_none(),
|
!has_project_config,
|
||||||
egui::TextEdit::singleline(&mut config_state.watch_pattern_text).desired_width(100.0),
|
egui::TextEdit::singleline(pattern_text).desired_width(100.0),
|
||||||
)
|
)
|
||||||
.on_disabled_hover_text(CONFIG_DISABLED_TEXT);
|
.on_disabled_hover_text(CONFIG_DISABLED_TEXT);
|
||||||
if ui
|
if ui
|
||||||
.add_enabled(state.project_config_info.is_none(), egui::Button::new("+").small())
|
.add_enabled(!has_project_config, egui::Button::new("+").small())
|
||||||
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
|
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
|
||||||
.clicked()
|
.clicked()
|
||||||
&& let Ok(glob) = Glob::new(&config_state.watch_pattern_text)
|
&& let Ok(glob) = Glob::new(pattern_text)
|
||||||
{
|
{
|
||||||
state.config.watch_patterns.push(glob);
|
patterns.push(glob);
|
||||||
state.watcher_change = true;
|
change = true;
|
||||||
config_state.watch_pattern_text.clear();
|
pattern_text.clear();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
change
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arch_config_window(
|
pub fn arch_config_window(
|
||||||
|
@ -142,6 +142,11 @@ impl DiffViewState {
|
|||||||
JobResult::ObjDiff(result) => {
|
JobResult::ObjDiff(result) => {
|
||||||
self.build = take(result);
|
self.build = take(result);
|
||||||
|
|
||||||
|
// Clear reload flag so that we don't reload the view immediately
|
||||||
|
if let Ok(mut state) = state.write() {
|
||||||
|
state.queue_reload = false;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: where should this go?
|
// TODO: where should this go?
|
||||||
if let Some(result) = self.post_build_nav.take() {
|
if let Some(result) = self.post_build_nav.take() {
|
||||||
self.current_view = result.view;
|
self.current_view = result.view;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user