Render

Trait Render 

Source
pub trait Render<T> {
    type Str<'a>: AsRef<str>
       where T: 'a;

    // Required method
    fn render<'a>(&self, item: &'a T) -> Self::Str<'a>;
}
Expand description

A trait which describes how to render objects for matching and display.

Some renderers for common types are already implemented in the render module. In many cases, the DisplayRenderer is particularly easy to use. This trait is also automatically implemented for closures which return Cow<'a, str>.

Rendering must be pure: for a given render implementation R and a item T, the call R::render(&self, &T) must depend only on the specific render instance and the specific item, and not any other state. Violation of this condition is normally only possible via interior mutability, global state, I/O, or unsafe code.

If purism is violated, internal index computations which depend on the rendered format will become invalid and the picker may panic or return incorrect results. Note that such errors are encapsulated within the picker and will not result in undefined behaviour.

§Examples

Here is a basic example for how one would implement a renderer for a DirEntry from the ignore crate.

use std::borrow::Cow;

use nucleo_picker::Render;
use ignore::DirEntry;

pub struct DirEntryRenderer;

impl Render<DirEntry> for DirEntryRenderer {
    type Str<'a> = Cow<'a, str>;

    fn render<'a>(&self, item: &'a DirEntry) -> Self::Str<'a> {
        item.path().to_string_lossy()
    }
}

Here is another example showing that a renderer can use internal (immutable) state to customize the rendered format.

use nucleo_picker::Render;

pub struct PrefixRenderer {
    prefix: String,
}

impl<T: AsRef<str>> Render<T> for PrefixRenderer {
    type Str<'a> = String
        where T: 'a;

    fn render<'a>(&self, item: &'a T) -> Self::Str<'a> {
        let mut rendered = String::new();
        rendered.push_str(&self.prefix);
        rendered.push_str(item.as_ref());
        rendered
    }
}

§Render considerations

The picker is capable of correctly displaying most Unicode data. Internally, Unicode width calculations are performed to keep track of the amount of space that it takes on the screen to display a given item.

The main exeption is control characters which are not newlines (\n or \r\n). Even visible control characters, such as tabs (\t) will cause issues: width calculations will most likely be incorrect since the amount of space a tab occupies depends on its position within the screen.

It is best to avoid such characters in your rendered format. If you do not have control over the incoming data, the most robust solution is likely to perform substitutions during rendering.

use std::borrow::Cow;

fn renderable(c: char) -> bool {
    !c.is_control() || c == '\n'
}

struct ControlReplaceRenderer;

impl<T: AsRef<str>> Render<T> for ControlReplaceRenderer {
    type Str<'a>
        = Cow<'a, str>
    where
        T: 'a;

    fn render<'a>(&self, item: &'a T) -> Self::Str<'a> {
        let mut str = Cow::Borrowed(item.as_ref());

        if str.contains(|c| !renderable(c)) {
            str.to_mut().retain(renderable);
        }

        str
    }
}

§Performance considerations

In the majority of situations, performance of the Render implementation is only relevant when sending the items to the picker, and not for generating the match list interactively. In particular, in the majority of situations, Render::render is called exactly once per item when it is sent to the picker.

The only exception to this rule occurs the value returned by Render::render contains non-ASCII characters. In this situation, it can happen that exceptionally slow Render implementations will reduce interactivity. A crude rule of thumb is that rendering a single item should take (in the worst case) at most 100μs. For comparison, display formatting a f64 takes around 100ns.

100μs is an extremely large amount of time in the vast majority of situations. If after benchmarking you determine that this is not the case for your Render implementation, and moreover your Render implementation outputs (in the majority of cases) non-ASCII Unicode, you can internally cache the render computation (at the cost of increased memory overhead):

pub struct Item<D> {
    data: D,
    /// the pre-computed rendered version of `data`
    rendered: String,
}

pub struct ItemRenderer;

impl<D> Render<Item<D>> for ItemRenderer {
    type Str<'a>
        = &'a str
    where
        D: 'a;

    fn render<'a>(&self, item: &'a Item<D>) -> Self::Str<'a> {
        &item.rendered
    }
}

Required Associated Types§

Source

type Str<'a>: AsRef<str> where T: 'a

The string type that T is rendered as, most commonly a &'a str, a Cow<'a, str>, or a String.

Required Methods§

Source

fn render<'a>(&self, item: &'a T) -> Self::Str<'a>

Render the given item as it should appear in the picker. See the trait-level docs for more detail.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementors§

Source§

impl<T, R: for<'a> Fn(&'a T) -> Cow<'a, str>> Render<T> for R

Source§

type Str<'a> = Cow<'a, str> where T: 'a

Source§

impl<T: ToString> Render<T> for DisplayRenderer

Source§

type Str<'a> = String where T: 'a

Source§

impl<T: AsRef<str>> Render<T> for StrRenderer

Source§

type Str<'a> = &'a str where T: 'a

Source§

impl<T: AsRef<Path>> Render<T> for PathRenderer

Source§

type Str<'a> = Cow<'a, str> where T: 'a