Skip to content

Commit

Permalink
Unindexed block completions
Browse files Browse the repository at this point in the history
  • Loading branch information
Feel-ix-343 committed Mar 21, 2024
1 parent 6892d6c commit a5697bb
Show file tree
Hide file tree
Showing 5 changed files with 416 additions and 88 deletions.
184 changes: 123 additions & 61 deletions src/completion/link_completer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,67 @@ use rayon::prelude::*;
use regex::Regex;
use tower_lsp::lsp_types::{CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionTextEdit, InsertTextFormat, Position, Range, TextEdit};

use crate::vault::{get_obsidian_ref_path, MDFile, MDHeading, MDIndexedBlock, Reference, Referenceable, Vault};
use crate::vault::{MDFile, MDHeading, MDIndexedBlock, Reference, Referenceable, Vault};

use super::{matcher::{fuzzy_match, fuzzy_match_completions, Matchable, OrderedCompletion}, Completable, Completer, Context};
use super::{matcher::{fuzzy_match_completions, Matchable, OrderedCompletion}, Completable, Completer, Context};

/// Range on a single line; assumes that the line number is known.
type LineRange = std::ops::Range<usize>;

pub struct MarkdownLinkCompleter<'a> {
/// The display text of a link to be completed
display: (String, LineRange),
pub display: (String, LineRange),
/// the filepath of the markdown link to be completed
path: (String, LineRange),
pub path: (String, LineRange),
/// the infile ref; the range is the whole span of the infile ref. (including the ^ for Block refs)
infile_ref: Option<(PartialInfileRef, LineRange)>,
pub infile_ref: Option<(PartialInfileRef, LineRange)>,

partial_link: (String, LineRange),
full_range: LineRange,
line_nr: usize,
file_path: std::path::PathBuf,
vault: &'a Vault
pub partial_link: (String, LineRange),
pub full_range: LineRange,
pub line_nr: usize,
pub file_path: std::path::PathBuf,
pub vault: &'a Vault
}

pub trait LinkCompleter<'a> : Completer<'a> {
fn completion_text_edit(&self, display: Option<&str>, refname: &str) -> CompletionTextEdit;
fn entered_refname(&self) -> String;
fn vault(&self) -> &'a Vault;
}

impl<'a> LinkCompleter<'a> for MarkdownLinkCompleter<'a> {

fn vault(&self) -> &'a Vault {
self.vault
}

fn entered_refname(&self) -> String {
format!("{}{}", self.path.0, self.infile_ref.as_ref().map(|infile| infile.0.to_string()).unwrap_or("".to_string()))
}

/// Will add <$1> to the refname if it contains spaces
fn completion_text_edit(&self, display: Option<&str>, refname: &str) -> CompletionTextEdit {


let link_ref_text = match refname.contains(' ') {
true => format!("<{}>", refname),
false => refname.to_owned()
};

CompletionTextEdit::Edit(TextEdit {
range: Range {
start: Position {
line: self.line_nr as u32,
character: self.full_range.start as u32,
},
end: Position {
line: self.line_nr as u32,
character: self.full_range.end as u32,
},
},
new_text: format!("[{}]({})", display.unwrap_or(""), link_ref_text)
})
}
}

impl<'a> Completer<'a> for MarkdownLinkCompleter<'a> {
Expand Down Expand Up @@ -125,17 +166,40 @@ impl<'a> Completer<'a> for MarkdownLinkCompleter<'a> {
let filtered = fuzzy_match_completions(&filter_text, completions);

filtered
}

/// The completions refname
type FilterParams = &'a str;

fn completion_filter_text(&self, params: Self::FilterParams) -> String {
let filter_text = format!(
"[{}]({}",
self.display.0,
params
);

filter_text

}
}

#[derive(Debug, Clone)]
enum PartialInfileRef {
pub enum PartialInfileRef {
HeadingRef(String),
/// The partial reference to a block, not including the ^ index
BlockRef(String)
}


impl ToString for PartialInfileRef {
fn to_string(&self) -> String {
match self {
Self::HeadingRef(string) => string.to_owned(),
Self::BlockRef(string) => format!("^{}", string)
}
}
}

impl PartialInfileRef {
fn completion_string(&self) -> String {
match self {
Expand Down Expand Up @@ -210,11 +274,11 @@ impl LinkCompletion<'_> {


impl<'a> Completable<'a, MarkdownLinkCompleter<'a>> for LinkCompletion<'a> {
fn completion(&self, markdown_link_completer: &MarkdownLinkCompleter) -> CompletionItem {
fn completions(&self, markdown_link_completer: &MarkdownLinkCompleter<'a>) -> impl Iterator<Item = CompletionItem> {

let label = self.match_string();

let MarkdownLinkCompleter { display, path: _, infile_ref: _, partial_link: _, full_range, line_nr, file_path: _, vault: _ } = markdown_link_completer;
let MarkdownLinkCompleter { display, path: _, infile_ref: _, partial_link: _, full_range: _, line_nr: _, file_path: _, vault: _ } = markdown_link_completer;

let link_infile_ref = match self {
File { mdfile: _, match_string: _ }
Expand All @@ -227,80 +291,43 @@ impl<'a> Completable<'a, MarkdownLinkCompleter<'a>> for LinkCompletion<'a> {
let binding = (display.0.as_str(), link_infile_ref);
let link_display_text = match binding {
("", Some(ref infile)) => &infile,
// Get the first heading of the file, if possible.
// Get the first heading of the file, if possible.
("", None) => match self {
Self::File { mdfile, match_string: _ } => mdfile.headings.get(0).map(|heading| heading.heading_text.as_str()).unwrap_or(""),
_ => ""
}
(display, _) => display,
};


let link_ref_text = match label.contains(' ') {
true => format!("<{}>", label),
false => label.to_owned()
};

let link_text = format!(
"[${{1:{}}}]({})",
let link_display_text = format!(
"${{1:{}}}",
link_display_text,
link_ref_text
);

let text_edit = markdown_link_completer.completion_text_edit(Some(&link_display_text), &label);

let text_edit = CompletionTextEdit::Edit(TextEdit {
range: Range {
start: Position {
line: *line_nr as u32,
character: full_range.start as u32,
},
end: Position {
line: *line_nr as u32,
character: full_range.end as u32,
},
},
new_text: link_text.to_string(),
});

let filter_text = format!(
"[{}]({}",
markdown_link_completer.display.0,
label
);

let filter_text = markdown_link_completer.completion_filter_text(label);

CompletionItem {
std::iter::once(CompletionItem {
insert_text_format: Some(InsertTextFormat::SNIPPET),
..self.default_completion(&label, text_edit, &filter_text)
}
})

}
}


impl<'a> Completable<'a, WikiLinkCompleter<'a>> for LinkCompletion<'a> {
fn completion(&self, completer: &WikiLinkCompleter<'a>) -> CompletionItem {
fn completions(&self, completer: &WikiLinkCompleter<'a>) -> impl Iterator<Item = CompletionItem> {

let refname = self.match_string();

let text_edit = completer.completion_text_edit(None, refname);

let text_edit = CompletionTextEdit::Edit(TextEdit {
range: Range {
start: Position {
line: completer.line as u32,
character: completer.index + 1 as u32, // index is right at the '[' in [[link]]; we want one more than that
},
end: Position {
line: completer.line as u32,
character: completer.character as u32,
},
},
new_text: refname.to_string(),
});

let filter_text = &refname;
let filter_text = completer.completion_filter_text(refname);

self.default_completion(&refname, text_edit, filter_text)
std::iter::once(self.default_completion(&refname, text_edit, &filter_text))
}
}

Expand All @@ -327,6 +354,36 @@ pub struct WikiLinkCompleter<'a> {
line: u32
}

impl<'a> LinkCompleter<'a> for WikiLinkCompleter<'a> {

fn vault(&self) -> &'a Vault {
self.vault
}

fn entered_refname(&self) -> String {
String::from_iter(&self.cmp_text)
}

fn completion_text_edit(&self, display: Option<&str>, refname: &str) -> CompletionTextEdit {

let text_edit = CompletionTextEdit::Edit(TextEdit {
range: Range {
start: Position {
line: self.line as u32,
character: self.index + 1 as u32, // index is right at the '[' in [[link]]; we want one more than that
},
end: Position {
line: self.line as u32,
character: self.character as u32,
},
},
new_text: format!("{}{}", refname, display.map(|display| format!("|{}", display)).unwrap_or("".to_string()))
});

text_edit
}
}

impl<'a> Completer<'a> for WikiLinkCompleter<'a> {


Expand Down Expand Up @@ -421,6 +478,11 @@ impl<'a> Completer<'a> for WikiLinkCompleter<'a> {
_ => vec![]
}
}

type FilterParams = &'a str;
fn completion_filter_text(&self, params: Self::FilterParams) -> String {
params.to_string()
}
}


Expand Down
8 changes: 4 additions & 4 deletions src/completion/matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ impl<'a, C: Completer<'a>, T: Completable<'a, C>> OrderedCompletion<'a, C, T> {
}

impl<'a, C: Completer<'a>, T: Completable<'a, C>> Completable<'a, C> for OrderedCompletion<'a, C, T> {
fn completion(&self, completer: &C) -> tower_lsp::lsp_types::CompletionItem {
let completion = self.completable.completion(completer);
fn completions(&self, completer: &C) -> impl Iterator<Item = tower_lsp::lsp_types::CompletionItem> {
let completions = self.completable.completions(completer);

CompletionItem {
completions.map(|completion| CompletionItem {
sort_text: Some(self.rank.to_string()),
..completion
}
})
}
}

Expand Down
31 changes: 20 additions & 11 deletions src/completion/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{path::{Path, PathBuf}, time::SystemTime};
use std::{iter::once, path::{Path, PathBuf}, time::SystemTime};

use itertools::Itertools;
use nanoid::nanoid;
Expand All @@ -22,29 +22,35 @@ use crate::{
},
};

use self::{link_completer::{MarkdownLinkCompleter, WikiLinkCompleter}, matcher::fuzzy_match};
use self::{link_completer::MarkdownLinkCompleter, matcher::fuzzy_match, unindexed_block_completer::UnindexedBlockCompleter};
use self::link_completer::WikiLinkCompleter;

mod link_completer;
mod matcher;
mod unindexed_block_completer;

#[derive(Clone, Copy)]
pub struct Context<'a>{
vault: &'a Vault,
opened_files: &'a [PathBuf],
}

trait Completer<'a> {
pub trait Completer<'a> : Sized {
fn construct(context: Context<'a>, path: &Path, line: usize, character: usize) -> Option<Self>
where Self: Sized;
where Self: Sized + Completer<'a>;

fn completions(&self) -> Vec<impl Completable<'a, Self>> where Self: Sized;

type FilterParams;
/// Completere like nvim-cmp are odd so manually define the filter text as a situational workaround
fn completion_filter_text(&self, params: Self::FilterParams) -> String;

// fn compeltion_resolve(&self, vault: &Vault, resolve_item: CompletionItem) -> Option<CompletionItem>;
}


trait Completable<'a, T: Completer<'a>> {
fn completion(&self, completer: &T) -> CompletionItem;
pub trait Completable<'a, T: Completer<'a>> : Sized {
fn completions(&self, completer: &T) -> impl Iterator<Item = CompletionItem>;
}

/// Range indexes for one line of the file; NOT THE WHOLE FILE
Expand Down Expand Up @@ -112,7 +118,9 @@ pub fn get_completions(
opened_files: initial_completion_files
};

run_completer::<MarkdownLinkCompleter>(completion_context, &path, params.text_document_position.position.line, params.text_document_position.position.character)
run_completer::<UnindexedBlockCompleter<MarkdownLinkCompleter>>(completion_context, &path, params.text_document_position.position.line, params.text_document_position.position.character)
.or_else(|| run_completer::<UnindexedBlockCompleter<WikiLinkCompleter>>(completion_context, &path, params.text_document_position.position.line, params.text_document_position.position.character))
.or_else(|| run_completer::<MarkdownLinkCompleter>(completion_context, &path, params.text_document_position.position.line, params.text_document_position.position.character))
.or_else(|| run_completer::<WikiLinkCompleter>(completion_context, &path, params.text_document_position.position.line, params.text_document_position.position.character))

}
Expand Down Expand Up @@ -760,15 +768,16 @@ pub fn get_completions(
fn run_completer<'a, T: Completer<'a>>(context: Context<'a>, path: &Path, line: u32, character: u32) -> Option<CompletionResponse> {

let completer = T::construct(context, path, line as usize, character as usize)?;
let completions = completer.completions();

let completions = completer.completions()
let completions = completions
.into_iter()
.take(50)
.map(|completable| completable.completion(&completer))
.collect_vec();
.map(|completable| completable.completions(&completer).collect::<Vec<_>>().into_iter()) // Hate this
.flatten()
.collect::<Vec<CompletionItem>>();

Some(CompletionResponse::List(CompletionList { is_incomplete: true, items: completions }))


}

Loading

0 comments on commit a5697bb

Please sign in to comment.