orgize/src/ast/link.rs

127 lines
5 KiB
Rust

use rowan::ast::{support, AstNode};
use super::{AffiliatedKeyword, Link, Paragraph, Token};
use crate::{syntax::SyntaxKind, SyntaxElement};
impl Link {
/// Returns link destination
///
/// ```rust
/// use orgize::{Org, ast::Link};
///
/// let link = Org::parse("[[#id]]").first_node::<Link>().unwrap();
/// assert_eq!(link.path(), "#id");
/// let link = Org::parse("[[https://google.com]]").first_node::<Link>().unwrap();
/// assert_eq!(link.path(), "https://google.com");
/// let link = Org::parse("[[https://google.com][Google]]").first_node::<Link>().unwrap();
/// assert_eq!(link.path(), "https://google.com");
/// ```
pub fn path(&self) -> Token {
support::token(&self.syntax, SyntaxKind::LINK_PATH).map_or_else(
|| {
debug_assert!(false, "link must contains LINK_PATH");
Token::default()
},
|e| Token(Some(e)),
)
}
/// Returns `true` if link contains description
///
/// ```rust
/// use orgize::{Org, ast::Link};
///
/// let link = Org::parse("[[https://google.com]]").first_node::<Link>().unwrap();
/// assert!(!link.has_description());
/// let link = Org::parse("[[https://google.com][Google]]").first_node::<Link>().unwrap();
/// assert!(link.has_description());
/// let link = Org::parse("[[https://example.com][*abc* /abc/]]").first_node::<Link>().unwrap();
/// assert!(link.has_description());
/// ```
pub fn has_description(&self) -> bool {
self.syntax()
.children_with_tokens()
.any(|e| e.kind() == SyntaxKind::L_BRACKET)
}
/// Returns parsed description
///
/// Returns empty iterator if this link doesn't contain description
///
/// ```rust
/// use orgize::{Org, ast::Link, SyntaxKind};
///
/// let link = Org::parse("[[https://google.com]]").first_node::<Link>().unwrap();
/// assert_eq!(link.description().count(), 0);
///
/// let link = Org::parse("[[https://google.com][Google]]").first_node::<Link>().unwrap();
/// let description = link.description().collect::<Vec<_>>();
/// assert_eq!((description[0].kind(), description[0].to_string()), (SyntaxKind::TEXT, "Google".into()));
///
/// let link = Org::parse("[[https://example.com][*abc* /abc/]]").first_node::<Link>().unwrap();
/// let description = link.description().collect::<Vec<_>>();
/// assert_eq!((description[0].kind(), description[0].to_string()), (SyntaxKind::BOLD, "*abc*".into()));
/// assert_eq!((description[2].kind(), description[2].to_string()), (SyntaxKind::ITALIC, "/abc/".into()));
/// ```
pub fn description(&self) -> impl Iterator<Item = SyntaxElement> {
self.syntax()
.children_with_tokens()
.skip_while(|e| e.kind() != SyntaxKind::L_BRACKET)
.skip(1)
.take_while(|e| e.kind() != SyntaxKind::R_BRACKET2)
}
/// Returns description raw string
///
/// Returns empty string if this link doesn't contain description
///
/// ```rust
/// use orgize::{Org, ast::Link};
///
/// let link = Org::parse("[[https://google.com]]").first_node::<Link>().unwrap();
/// assert_eq!(link.description_raw(), "");
/// let link = Org::parse("[[https://google.com][Google]]").first_node::<Link>().unwrap();
/// assert_eq!(link.description_raw(), "Google");
/// let link = Org::parse("[[https://example.com][*abc* /abc/]]").first_node::<Link>().unwrap();
/// assert_eq!(link.description_raw(), "*abc* /abc/");
/// ```
pub fn description_raw(&self) -> String {
self.description()
.fold(String::new(), |acc, e| acc + &e.to_string())
}
/// Returns `true` if link is an image link
///
/// ```rust
/// use orgize::{Org, ast::Link};
///
/// let link = Org::parse("[[https://google.com]]").first_node::<Link>().unwrap();
/// assert!(!link.is_image());
/// let link = Org::parse("[[file:/home/dominik/images/jupiter.jpg]]").first_node::<Link>().unwrap();
/// assert!(link.is_image());
/// ```
pub fn is_image(&self) -> bool {
const IMAGE_SUFFIX: &[&str] = &[
// https://github.com/bzg/org-mode/blob/7de1e818d5fbe6a05c6b1a007eed07dc27e7246b/lisp/ox.el#L253
".png", ".jpeg", ".jpg", ".gif", ".tiff", ".tif", ".xbm", ".xpm", ".pbm", ".pgm",
".ppm", ".webp", ".avif", ".svg",
];
let path = self.path();
IMAGE_SUFFIX.iter().any(|e| path.ends_with(e)) && !self.has_description()
}
/// Returns caption keyword in this link
///
/// ```rust
/// use orgize::{Org, ast::Link};
///
/// let link = Org::parse("#+CAPTION: image link\n[[file:/home/dominik/images/jupiter.jpg]]").first_node::<Link>().unwrap();
/// assert_eq!(link.caption().unwrap().value().unwrap(), " image link");
/// ```
pub fn caption(&self) -> Option<AffiliatedKeyword> {
// TODO: support other element type
Paragraph::cast(self.syntax.parent()?.clone())?.caption()
}
}