Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
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.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ walkdir = "2.5.0"

html-escape = "0.2.13"
html5ever = "0.38.0"
indexmap = "2.13.0"
kuchikikiki = "0.9.2"
lightningcss = { version = "1.0.0-alpha.71", default-features = false }
markup5ever = "0.38.0"
Expand Down
94 changes: 44 additions & 50 deletions crates/ndg-config/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use std::{
collections::HashMap,
fs,
path::{Path, PathBuf},
sync::OnceLock,
};

use ndg_macros::Configurable;
Expand Down Expand Up @@ -449,22 +448,18 @@ impl Config {
"The 'opengraph' config field is deprecated. Use 'meta.opengraph' \
instead."
);
if let Some(ref mut og) = self.opengraph {
og.extend(other_og);
} else {
self.opengraph = Some(other_og);
}

// Later value wins
self.opengraph = Some(other_og);
}
if let Some(other_meta) = other.meta_tags.take() {
log::warn!(
"The 'meta_tags' config field is deprecated. Use 'meta.tags' \
instead."
);
if let Some(ref mut meta) = self.meta_tags {
meta.extend(other_meta);
} else {
self.meta_tags = Some(other_meta);
}

// Later value wins
self.meta_tags = Some(other_meta);
}
}

Expand Down Expand Up @@ -530,52 +525,51 @@ impl Config {
None
}

/// Search for config files in common locations
/// Search for config files in common locations.
///
/// This performs a fresh filesystem lookup on every call, and the result is
/// not cached so that tests calling from different working directories each
/// see the correct config for their directory.
#[must_use]
pub fn find_config_file() -> Option<PathBuf> {
static RESULT: OnceLock<Option<PathBuf>> = OnceLock::new();
RESULT
.get_or_init(|| {
let config_filenames = [
"ndg.toml",
"ndg.json",
".ndg.toml",
".ndg.json",
".config/ndg.toml",
".config/ndg.json",
];

let current_dir = std::env::current_dir().ok()?;
for filename in &config_filenames {
let config_path = current_dir.join(filename);
if config_path.exists() {
return Some(config_path);
}
}
let config_filenames = [
"ndg.toml",
"ndg.json",
".ndg.toml",
".ndg.json",
".config/ndg.toml",
".config/ndg.json",
];

let current_dir = std::env::current_dir().ok()?;
for filename in &config_filenames {
let config_path = current_dir.join(filename);
if config_path.exists() {
return Some(config_path);
}
}

if let Ok(xdg_config_home) = std::env::var("XDG_CONFIG_HOME") {
let xdg_config_dir = PathBuf::from(xdg_config_home);
for filename in &["ndg.toml", "ndg.json"] {
let config_path = xdg_config_dir.join(filename);
if config_path.exists() {
return Some(config_path);
}
}
if let Ok(xdg_config_home) = std::env::var("XDG_CONFIG_HOME") {
let xdg_config_dir = PathBuf::from(xdg_config_home).join("ndg");
for filename in &["config.toml", "config.json"] {
let config_path = xdg_config_dir.join(filename);
if config_path.exists() {
return Some(config_path);
}
}
}

if let Ok(home) = std::env::var("HOME") {
let home_config_dir = PathBuf::from(home).join(".config").join("ndg");
for filename in &["config.toml", "config.json"] {
let config_path = home_config_dir.join(filename);
if config_path.exists() {
return Some(config_path);
}
}
if let Ok(home) = std::env::var("HOME") {
let home_config_dir = PathBuf::from(home).join(".config").join("ndg");
for filename in &["config.toml", "config.json"] {
let config_path = home_config_dir.join(filename);
if config_path.exists() {
return Some(config_path);
}
}
}

None
})
.clone()
None
}

/// Validate all paths specified in the configuration
Expand Down
15 changes: 11 additions & 4 deletions crates/ndg-config/src/sidebar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,11 @@ impl SidebarMatch {
/// always be validated/compiled via `SidebarConfig::validate()`.
#[must_use]
pub fn matches(&self, path_str: &str, title_str: &str) -> bool {
// A rule with no conditions matches nothing; require at least one filter.
if self.path.is_none() && self.title.is_none() {
return false;
}

// Check path matching
if let Some(ref path_match) = self.path {
// Check exact path match
Expand Down Expand Up @@ -627,6 +632,11 @@ impl OptionsMatch {
/// always be validated/compiled via `OptionsConfig::validate()`.
#[must_use]
pub fn matches(&self, option_name: &str) -> bool {
// A rule with no conditions matches nothing.
if self.name.is_none() {
return false;
}

// Check name matching
if let Some(ref name_match) = self.name {
// Check exact name match
Expand Down Expand Up @@ -673,10 +683,7 @@ impl OptionsMatch {
/// Check if this option should be hidden from the TOC.
#[must_use]
pub const fn is_hidden(&self) -> bool {
match self.hidden {
Some(hidden) => hidden,
None => false,
}
matches!(self.hidden, Some(true))
}
}

Expand Down
16 changes: 8 additions & 8 deletions crates/ndg-config/src/templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,6 @@ footer_text = "Generated with ndg"
# Whether to generate anchors for headings
generate_anchors = true

# Search configuration
[search]
# Whether to generate a search index
enable = true

# Maximum heading level to index
max_heading_level = 3

# Depth of parent categories in options TOC
options_toc_depth = 2

Expand All @@ -94,6 +86,14 @@ revision = "main"
# Additional meta tags to inject into the HTML head (example: { description = "Docs", keywords = "nix,docs" })
# meta_tags = { description = "Documentation for My Project", keywords = "nix,docs,example" }

# Search configuration
[search]
# Whether to generate a search index
enable = true

# Maximum heading level to index
max_heading_level = 3

# Sidebar configuration
# [sidebar]
# Enable numbering for sidebar items
Expand Down
1 change: 1 addition & 0 deletions crates/ndg-html/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ ndg-templates.workspace = true
ndg-utils.workspace = true

color-eyre.workspace = true
indexmap.workspace = true
grass.workspace = true
html-escape.workspace = true
log.workspace = true
Expand Down
54 changes: 18 additions & 36 deletions crates/ndg-html/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::{
};

use color_eyre::eyre::{Context, Result};
use indexmap::IndexMap;
use log::debug;
use ndg_config::Config;
use ndg_manpage::types::NixOption;
Expand Down Expand Up @@ -168,43 +169,26 @@ pub fn process_options(config: &Config, options_path: &Path) -> Result<()> {
}
}

// Sort options by priority (enable > package > other)
let mut sorted_options: Vec<_> = options.into_iter().collect();
sorted_options.sort_by(|(name_a, _), (name_b, _)| {
// Calculate priority for name_a
let priority_a = if name_a.starts_with("enable") {
0
} else if name_a.starts_with("package") {
1
} else {
2
};

// Calculate priority for name_b
let priority_b = if name_b.starts_with("enable") {
0
} else if name_b.starts_with("package") {
1
} else {
2
// Sort options by priority (enable > package > other), with secondary
// alphabetical sort within each priority group.
let mut sorted: Vec<_> = options.into_iter().collect();
sorted.sort_by(|(name_a, _), (name_b, _)| {
let priority = |name: &str| {
if name.starts_with("enable") {
0
} else if name.starts_with("package") {
1
} else {
2
}
};

// Compare by priority first, then by name
priority_a.cmp(&priority_b).then_with(|| name_a.cmp(name_b))
priority(name_a)
.cmp(&priority(name_b))
.then_with(|| name_a.cmp(name_b))
});

// Convert back to HashMap preserving the new order
let customized_options = sorted_options
.iter()
.map(|(key, opt)| {
let option_clone = opt.clone();
(key.clone(), option_clone)
})
.map(|(key, mut opt)| {
opt.name = html_escape::encode_text(&opt.name).to_string();
(key, opt)
})
.collect();
let customized_options: IndexMap<String, NixOption> =
sorted.into_iter().collect();

// Render options page
let html = template::render_options(config, &customized_options)?;
Expand Down Expand Up @@ -247,9 +231,7 @@ fn format_location(
let path_str = path.as_str();

if path_str.starts_with('/') {
// Local filesystem path handling
let url = format!("file://{path_str}");

if path_str.contains("nixops") && path_str.contains("/nix/") {
let suffix_index = path_str.find("/nix/").map_or(0, |i| i + 5);
let suffix = &path_str[suffix_index..];
Expand Down
1 change: 0 additions & 1 deletion crates/ndg-html/src/search.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::{
clone::Clone,
collections::{HashMap, HashSet},
fs,
path::PathBuf,
Expand Down
Loading
Loading