pepper-plugin-lsp 0.4.0

Language Server Protocol plugin for Pepper editor
Documentation
use std::path::Path;

use pepper::{
    buffer::{parse_path_and_position, BufferProperties},
    buffer_position::BufferPosition,
    client::ClientHandle,
    cursor::Cursor,
    editor::{Editor, EditorContext, EditorControlFlow, KeysIterator},
    editor_utils::{MessageKind, ReadLinePoll},
    mode::ModeKind,
    picker::EntrySource,
    plugin::PluginHandle,
    word_database::WordIndicesIter,
};

use crate::{
    client::{Client, ClientOperation},
    LspPlugin,
};

pub fn enter_definition_mode(editor: &mut Editor, plugin_handle: PluginHandle) -> ClientOperation {
    fn on_client_keys(
        ctx: &mut EditorContext,
        client_handle: ClientHandle,
        _: &mut KeysIterator,
        poll: ReadLinePoll,
    ) -> Option<EditorControlFlow> {
        match poll {
            ReadLinePoll::Pending => Some(EditorControlFlow::Continue),
            ReadLinePoll::Submitted => {
                if let Some((_, entry)) = ctx.editor.picker.current_entry(&ctx.editor.word_database)
                {
                    let (path, position) = parse_path_and_position(entry);
                    let position = match position {
                        Some(position) => position,
                        None => BufferPosition::zero(),
                    };

                    let path = ctx.editor.string_pool.acquire_with(path);
                    match ctx.editor.buffer_view_handle_from_path(
                        client_handle,
                        Path::new(&path),
                        BufferProperties::text(),
                        false,
                    ) {
                        Ok(buffer_view_handle) => {
                            let client = ctx.clients.get_mut(client_handle);
                            client.set_buffer_view_handle(
                                Some(buffer_view_handle),
                                &ctx.editor.buffer_views,
                            );

                            let mut cursors = ctx
                                .editor
                                .buffer_views
                                .get_mut(buffer_view_handle)
                                .cursors
                                .mut_guard();
                            cursors.clear();
                            cursors.add(Cursor {
                                anchor: position,
                                position,
                            });
                        }
                        Err(error) => ctx
                            .editor
                            .status_bar
                            .write(MessageKind::Error)
                            .fmt(format_args!("{}", error)),
                    }
                    ctx.editor.string_pool.release(path);
                }
                ctx.editor.enter_mode(ModeKind::default());
                Some(EditorControlFlow::Continue)
            }
            ReadLinePoll::Canceled => {
                ctx.editor.enter_mode(ModeKind::default());
                Some(EditorControlFlow::Continue)
            }
        }
    }

    editor.read_line.set_prompt("definition:");
    editor.picker.filter(WordIndicesIter::empty(), "");
    editor.picker.move_cursor(0);

    if editor.picker.len() > 0 {
        let state = &mut editor.mode.picker_state;
        state.on_client_keys = on_client_keys;
        state.plugin_handle = Some(plugin_handle);
        editor.enter_mode(ModeKind::Picker);
    }

    ClientOperation::EnteredPickerMode
}

pub fn enter_code_action_mode(
    editor: &mut Editor,
    plugin_handle: PluginHandle,
    client: &mut Client,
) -> ClientOperation {
    fn on_client_keys(
        ctx: &mut EditorContext,
        _: ClientHandle,
        _: &mut KeysIterator,
        poll: ReadLinePoll,
    ) -> Option<EditorControlFlow> {
        match poll {
            ReadLinePoll::Pending => Some(EditorControlFlow::Continue),
            ReadLinePoll::Submitted => {
                if let Some(handle) = ctx.editor.mode.picker_state.plugin_handle {
                    let lsp = ctx.plugins.get_as::<LspPlugin>(handle);
                    if let Some(client) =
                        lsp.picker_client_handle.take().and_then(|h| lsp.get_mut(h))
                    {
                        let index = match ctx.editor.picker.current_entry(&ctx.editor.word_database)
                        {
                            Some((EntrySource::Custom(i), _)) => i,
                            _ => 0,
                        };
                        client.finish_code_action(&mut ctx.editor, index);
                    }
                }

                ctx.editor.enter_mode(ModeKind::default());
                Some(EditorControlFlow::Continue)
            }
            ReadLinePoll::Canceled => {
                if let Some(handle) = ctx.editor.mode.picker_state.plugin_handle {
                    let lsp = ctx.plugins.get_as::<LspPlugin>(handle);
                    if let Some(client) =
                        lsp.picker_client_handle.take().and_then(|h| lsp.get_mut(h))
                    {
                        client.cancel_current_request();
                    }
                }

                ctx.editor.enter_mode(ModeKind::default());
                Some(EditorControlFlow::Continue)
            }
        }
    }

    editor.read_line.set_prompt("code action:");
    editor.picker.filter(WordIndicesIter::empty(), "");
    editor.picker.move_cursor(0);

    if editor.picker.len() > 0 {
        let state = &mut editor.mode.picker_state;
        state.on_client_keys = on_client_keys;
        state.plugin_handle = Some(plugin_handle);
        editor.enter_mode(ModeKind::Picker);
        ClientOperation::EnteredPickerMode
    } else {
        client.cancel_current_request();
        ClientOperation::None
    }
}

pub fn enter_document_symbol_mode(
    editor: &mut Editor,
    plugin_handle: PluginHandle,
    client: &mut Client,
) -> ClientOperation {
    fn on_client_keys(
        ctx: &mut EditorContext,
        client_handle: ClientHandle,
        _: &mut KeysIterator,
        poll: ReadLinePoll,
    ) -> Option<EditorControlFlow> {
        match poll {
            ReadLinePoll::Pending => Some(EditorControlFlow::Continue),
            ReadLinePoll::Submitted => {
                if let Some(handle) = ctx.editor.mode.picker_state.plugin_handle {
                    let lsp = ctx.plugins.get_as::<LspPlugin>(handle);
                    if let Some(client) =
                        lsp.picker_client_handle.take().and_then(|h| lsp.get_mut(h))
                    {
                        let index = match ctx.editor.picker.current_entry(&ctx.editor.word_database)
                        {
                            Some((EntrySource::Custom(i), _)) => i,
                            _ => 0,
                        };
                        client.finish_document_symbols(
                            &mut ctx.editor,
                            &mut ctx.clients,
                            client_handle,
                            index,
                        );
                    }
                }

                ctx.editor.enter_mode(ModeKind::default());
                Some(EditorControlFlow::Continue)
            }
            ReadLinePoll::Canceled => {
                if let Some(handle) = ctx.editor.mode.picker_state.plugin_handle {
                    let lsp = ctx.plugins.get_as::<LspPlugin>(handle);
                    if let Some(client) =
                        lsp.picker_client_handle.take().and_then(|h| lsp.get_mut(h))
                    {
                        client.cancel_current_request();
                    }
                }

                ctx.editor.enter_mode(ModeKind::default());
                Some(EditorControlFlow::Continue)
            }
        }
    }

    editor.read_line.set_prompt("document symbol:");
    editor.picker.filter(WordIndicesIter::empty(), "");
    editor.picker.move_cursor(0);

    if editor.picker.len() > 0 {
        let state = &mut editor.mode.picker_state;
        state.on_client_keys = on_client_keys;
        state.plugin_handle = Some(plugin_handle);
        editor.enter_mode(ModeKind::Picker);
        ClientOperation::EnteredPickerMode
    } else {
        client.cancel_current_request();
        ClientOperation::None
    }
}

pub fn enter_workspace_symbol_mode(
    editor: &mut Editor,
    plugin_handle: PluginHandle,
    client: &mut Client,
) -> ClientOperation {
    fn on_client_keys(
        ctx: &mut EditorContext,
        client_handle: ClientHandle,
        _: &mut KeysIterator,
        poll: ReadLinePoll,
    ) -> Option<EditorControlFlow> {
        match poll {
            ReadLinePoll::Pending => Some(EditorControlFlow::Continue),
            ReadLinePoll::Submitted => {
                if let Some(handle) = ctx.editor.mode.picker_state.plugin_handle {
                    let lsp = ctx.plugins.get_as::<LspPlugin>(handle);
                    if let Some(client) =
                        lsp.picker_client_handle.take().and_then(|h| lsp.get_mut(h))
                    {
                        let index = match ctx.editor.picker.current_entry(&ctx.editor.word_database)
                        {
                            Some((EntrySource::Custom(i), _)) => i,
                            _ => 0,
                        };
                        client.finish_workspace_symbols(
                            &mut ctx.editor,
                            &mut ctx.clients,
                            client_handle,
                            index,
                        );
                    }
                }

                ctx.editor.enter_mode(ModeKind::default());
                Some(EditorControlFlow::Continue)
            }
            ReadLinePoll::Canceled => {
                if let Some(handle) = ctx.editor.mode.picker_state.plugin_handle {
                    let lsp = ctx.plugins.get_as::<LspPlugin>(handle);
                    if let Some(client) =
                        lsp.picker_client_handle.take().and_then(|h| lsp.get_mut(h))
                    {
                        client.cancel_current_request();
                    }
                }

                ctx.editor.enter_mode(ModeKind::default());
                Some(EditorControlFlow::Continue)
            }
        }
    }

    editor.read_line.set_prompt("workspace symbol:");
    editor.picker.filter(WordIndicesIter::empty(), "");
    editor.picker.move_cursor(0);

    if editor.picker.len() > 0 {
        let state = &mut editor.mode.picker_state;
        state.on_client_keys = on_client_keys;
        state.plugin_handle = Some(plugin_handle);
        editor.enter_mode(ModeKind::Picker);
        ClientOperation::EnteredPickerMode
    } else {
        client.cancel_current_request();
        ClientOperation::None
    }
}