use std::io::Write;
use crate::error::RenderResult as Result;
use pulldown_cmark::{CowStr, LinkType};
use tracing::{event, Level};
use super::state::{InlineAttrs, InlineState, StackedState, State, StateAndData, StateStack};
use super::write::write_styled;
use super::StateData;
use crate::references::UrlBase;
use crate::resources::ResourceUrlHandler;
use crate::terminal::capabilities::StyleCapability;
use crate::terminal::osc::set_link_url;
use crate::theme::CombineStyle;
use crate::{Environment, Settings};
#[allow(clippy::too_many_arguments)]
pub(super) fn start<'a, W: Write>(
writer: &mut W,
settings: &Settings,
environment: &Environment,
resource_handler: &dyn ResourceUrlHandler,
stack: StateStack,
state: InlineState,
attrs: InlineAttrs,
link_type: LinkType,
dest_url: CowStr<'a>,
title: CowStr<'a>,
data: StateData<'a>,
) -> Result<StateAndData<StateData<'a>>> {
let InlineAttrs {
style,
indent,
quote_bar_cols,
} = attrs.clone();
let resolved_link = environment.resolve_reference(&dest_url);
let image_state = match (settings.terminal_capabilities.image, resolved_link) {
(Some(capability), Some(ref url)) => {
let result = if settings.multiplexer == crate::Multiplexer::None {
capability.image_protocol().write_inline_image(
writer,
&resource_handler,
url,
settings.terminal_size,
)
} else {
let mut buffer = Vec::with_capacity(4096);
let inner = capability.image_protocol().write_inline_image(
&mut buffer,
&resource_handler,
url,
settings.terminal_size,
);
inner.and_then(|()| settings.multiplexer.write_passthrough(writer, &buffer))
};
result
.map_err(|error| {
event!(
Level::ERROR,
?error,
%url,
"failed to render image with capability {:?}: {:#}",
capability,
error
);
error
})
.map(|()| StackedState::RenderedImage)
.ok()
}
(None, Some(url)) => {
if let InlineState::InlineLink = state {
event!(Level::WARN, url = %url, "Terminal does not support images, want to render image as link but cannot: Already inside a link");
None
} else {
event!(Level::INFO, url = %url, "Terminal does not support images, rendering image as link");
match settings.terminal_capabilities.style {
Some(StyleCapability::Ansi) => {
set_link_url(writer, url, &environment.hostname)?;
Some(StackedState::Inline(
InlineState::InlineLink,
InlineAttrs {
indent,
style: settings.theme.image_link_style.on_top_of(&style),
quote_bar_cols: quote_bar_cols.clone(),
},
))
}
None => None,
}
}
}
(_, None) => None,
};
let (image_state, data) = match image_state {
Some(s) => (s, data),
None => {
event!(
Level::WARN,
"Rendering image {} as inline text, without link",
dest_url
);
let style = if let InlineState::InlineLink = state {
style
} else {
settings.theme.image_link_style.on_top_of(&style)
};
let s = StackedState::Inline(
InlineState::InlineText,
InlineAttrs {
style,
indent,
quote_bar_cols,
},
);
(s, data.push_pending_link(link_type, dest_url, title))
}
};
stack
.push(StackedState::Inline(state, attrs))
.current(image_state)
.and_data(data)
.ok()
}
pub(super) fn end_reference<'a, W: Write>(
writer: &mut W,
settings: &Settings,
stack: StateStack,
attrs: InlineAttrs,
data: StateData<'a>,
) -> Result<StateAndData<StateData<'a>>> {
let (data, link) = data.pop_pending_link();
let (data, index) =
data.add_link_reference(link.dest_url, link.title, settings.theme.image_link_style);
write_styled(
writer,
&settings.terminal_capabilities,
&settings.theme.image_link_style.on_top_of(&attrs.style),
format!("[{index}]"),
)?;
stack.pop().and_data(data).ok()
}
pub(super) fn handle_rendered_image<'a>(
stack: StateStack,
data: StateData<'a>,
event: pulldown_cmark::Event<'a>,
) -> Result<StateAndData<StateData<'a>>> {
use pulldown_cmark::Event::{End, Start};
use pulldown_cmark::Tag::Image;
use pulldown_cmark::TagEnd;
match event {
Start(Image { .. }) => stack
.push(StackedState::RenderedImage)
.current(StackedState::RenderedImage)
.and_data(data)
.ok(),
End(TagEnd::Image) => stack.pop().and_data(data).ok(),
_ => State::Stacked(stack, StackedState::RenderedImage)
.and_data(data)
.ok(),
}
}