Add builtin tldr tealdeer page (#472)

Fixes #218.
This commit is contained in:
Pavel Timofeev 2026-04-13 18:31:31 -04:00 committed by GitHub
commit b19517097a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 89 additions and 16 deletions

View file

@ -29,3 +29,5 @@ Options:
-h, --help Print help -h, --help Print help
To view the user documentation, please visit https://tealdeer-rs.github.io/tealdeer/. To view the user documentation, please visit https://tealdeer-rs.github.io/tealdeer/.
To view usage examples, run tldr tldr or tldr tealdeer.

42
pages/tealdeer.md Normal file
View file

@ -0,0 +1,42 @@
# tldr
> This is a builtin page that shows information for your installed tealdeer version.
> More information: <https://tealdeer-rs.github.io/tealdeer/>.
> This page shows tealdeer specific functionality. See tldr tldr for more examples.
- Render a local markdown file as a tldr page:
`tldr --render {{path/to/file.md}}`
- Show the raw markdown source of a page instead of rendering it:
`tldr --raw {{command}}`
- Show file and directory paths used by tealdeer:
`tldr --show-paths`
- Create an initial config file:
`tldr --seed-config`
- Override config file location:
`tldr --config-path <FILE>`
- Open a custom page for a command in `$EDITOR` (creates it if it doesn't exist):
`tldr --edit-page {{command}}`
- Open a custom patch for a command in `$EDITOR` (appended to the existing page):
`tldr --edit-patch {{command}}`
- Clear the local cache:
`tldr --clear-cache`
- If auto update is configured, disable it for this run:
`tldr --no-auto-update`

View file

@ -1,6 +1,6 @@
use std::{ use std::{
fs::{self, File}, fs::{self, File},
io::{BufReader, Cursor, ErrorKind, Read}, io::{Cursor, ErrorKind, Read},
path::{Path, PathBuf}, path::{Path, PathBuf},
time::{Duration, SystemTime}, time::{Duration, SystemTime},
}; };
@ -277,12 +277,12 @@ impl PageLookupResult {
self self
} }
/// Create a buffered reader that sequentially reads from the page and the /// Create a reader that sequentially reads from the page and the
/// patch, as if they were concatenated. /// patch, as if they were concatenated.
/// ///
/// This will return an error if either the page file or the patch file /// This will return an error if either the page file or the patch file
/// cannot be opened. /// cannot be opened.
pub fn reader(&self) -> Result<BufReader<Box<dyn Read>>> { pub fn reader(&self) -> Result<Box<dyn Read>> {
// Open page file // Open page file
let page_file = File::open(&self.page_path) let page_file = File::open(&self.page_path)
.with_context(|| format!("Could not open page file at {}", self.page_path.display()))?; .with_context(|| format!("Could not open page file at {}", self.page_path.display()))?;
@ -302,11 +302,11 @@ impl PageLookupResult {
// the page and patch files and that will read them sequentially, // the page and patch files and that will read them sequentially,
// because it avoids the boxing below. However, the performance impact // because it avoids the boxing below. However, the performance impact
// would first need to be shown to be significant using a benchmark. // would first need to be shown to be significant using a benchmark.
Ok(BufReader::new(if let Some(patch_file) = patch_file_opt { Ok(if let Some(patch_file) = patch_file_opt {
Box::new(page_file.chain(&b"\n"[..]).chain(patch_file)) as Box<dyn Read> Box::new(page_file.chain(&b"\n"[..]).chain(patch_file)) as Box<dyn Read>
} else { } else {
Box::new(page_file) as Box<dyn Read> Box::new(page_file) as Box<dyn Read>
})) })
} }
} }

View file

@ -18,7 +18,9 @@ use crate::types::{ColorOptions, PlatformType};
{usage-heading} {usage} {usage-heading} {usage}
{all-args}{after-help}", {all-args}{after-help}",
after_help = "To view the user documentation, please visit https://tealdeer-rs.github.io/tealdeer/.", after_help = "To view the user documentation, please visit https://tealdeer-rs.github.io/tealdeer/.
To view usage examples, run tldr tldr or tldr tealdeer.",
arg_required_else_help = true, arg_required_else_help = true,
help_expected = true, help_expected = true,
group = ArgGroup::new("command_or_file").args(&["command", "render"]), group = ArgGroup::new("command_or_file").args(&["command", "render"]),

View file

@ -68,6 +68,8 @@ const APP_INFO: AppInfo = AppInfo {
name: NAME, name: NAME,
author: NAME, author: NAME,
}; };
static TEALDEER_PAGE: &str =
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/pages/tealdeer.md"));
/// Clear the cache /// Clear the cache
fn clear_cache(cache: Cache, quietly: bool) -> Result<()> { fn clear_cache(cache: Cache, quietly: bool) -> Result<()> {
@ -258,8 +260,20 @@ fn try_main(args: Cli, enable_styles: bool) -> Result<ExitCode> {
// If a local file was passed in, render it and exit // If a local file was passed in, render it and exit
if let Some(file) = args.render { if let Some(file) = args.render {
let path = PageLookupResult::with_page(file); let reader = PageLookupResult::with_page(file).reader()?;
print_page(&path, args.raw, enable_styles, args.pager, &config)?; print_page(reader, args.raw, enable_styles, args.pager, &config)?;
return Ok(ExitCode::SUCCESS);
}
// The tealdeer page is embedded in the binary, no cache needed
if command == "tealdeer" {
print_page(
TEALDEER_PAGE.as_bytes(),
args.raw,
enable_styles,
args.pager,
&config,
)?;
return Ok(ExitCode::SUCCESS); return Ok(ExitCode::SUCCESS);
} }
@ -407,7 +421,7 @@ fn try_main(args: Cli, enable_styles: bool) -> Result<ExitCode> {
); );
} }
let Some(lookup_result) = cache.find_page(&command) else { let Some(result) = cache.find_page(&command) else {
if !args.quiet { if !args.quiet {
print_warning( print_warning(
enable_styles, enable_styles,
@ -419,11 +433,16 @@ fn try_main(args: Cli, enable_styles: bool) -> Result<ExitCode> {
), ),
); );
} }
return Ok(ExitCode::FAILURE); return Ok(ExitCode::FAILURE);
}; };
print_page(&lookup_result, args.raw, enable_styles, args.pager, &config)?; print_page(
result.reader()?,
args.raw,
enable_styles,
args.pager,
&config,
)?;
} }
Ok(ExitCode::SUCCESS) Ok(ExitCode::SUCCESS)

View file

@ -1,12 +1,11 @@
//! Functions for printing pages to the terminal //! Functions for printing pages to the terminal
use std::io::{self, BufRead, Write}; use std::io::{self, BufRead, BufReader, Read, Write};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use yansi::Paint; use yansi::Paint;
use crate::{ use crate::{
cache::PageLookupResult,
config::{Config, StyleConfig}, config::{Config, StyleConfig},
formatter::{highlight_lines, PageSnippet}, formatter::{highlight_lines, PageSnippet},
line_iterator::LineIterator, line_iterator::LineIterator,
@ -30,14 +29,13 @@ fn configure_pager(enable_styles: bool) {
/// Print page by path /// Print page by path
pub fn print_page( pub fn print_page(
lookup_result: &PageLookupResult, reader: impl Read,
enable_markdown: bool, enable_markdown: bool,
enable_styles: bool, enable_styles: bool,
use_pager: bool, use_pager: bool,
config: &Config, config: &Config,
) -> Result<()> { ) -> Result<()> {
// Create reader from file(s) let reader = BufReader::new(reader);
let reader = lookup_result.reader()?;
// Configure pager if applicable // Configure pager if applicable
if use_pager || config.display.use_pager { if use_pager || config.display.use_pager {

View file

@ -301,6 +301,16 @@ fn test_missing_cache() {
.stderr(contains("Page cache not found. Please run `tldr --update`")); .stderr(contains("Page cache not found. Please run `tldr --update`"));
} }
#[test]
fn test_tealdeer_page_works_without_cache() {
TestEnv::new()
.command()
.args(["tealdeer"])
.assert()
.success()
.stdout(contains("for your installed tealdeer version"));
}
#[cfg_attr(feature = "ignore-online-tests", ignore = "online test")] #[cfg_attr(feature = "ignore-online-tests", ignore = "online test")]
#[test] #[test]
fn test_update_cache_default_features() { fn test_update_cache_default_features() {