Skip to content

Commit

Permalink
support headings
Browse files Browse the repository at this point in the history
  • Loading branch information
Feel-ix-343 committed Jun 13, 2024
1 parent a9bd6b5 commit 9db3944
Show file tree
Hide file tree
Showing 10 changed files with 144 additions and 215 deletions.
10 changes: 10 additions & 0 deletions TestFiles/Test.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ Configure your daily note formatting in the config file

today is 2024-03-27

[[testTest.md#Heading 1

[[Test#Heading 1]]

[[TestTesttest]]

[[Test#Heading 1]]

[[Random File#Heading 1]]

[next Tuesday](2024-04-02)

[tomorrow](2024-03-28)
Expand Down
205 changes: 72 additions & 133 deletions crates/completions/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,168 +1,107 @@
#![feature(anonymous_lifetime_in_impl_trait)]

use std::{ops::{Deref, Range}, path::{Path, PathBuf}};
mod parser;
mod querier;

use std::{
ops::{Deref, Range},
path::{Path, PathBuf},
};

use moxide_config::Settings;
use parser::{LinkInfo, Parser};
use querier::{NamedEntity, NamedEntityInfo, Querier};
use rayon::prelude::*;
use regex::Regex;
use tower_lsp::lsp_types::{CompletionItem, CompletionList, CompletionParams, CompletionResponse};
use tower_lsp::lsp_types::{
CompletionItem, CompletionList, CompletionParams, CompletionResponse, CompletionTextEdit,
TextEdit,
};
use vault::Vault;
use moxide_config::Settings;


pub fn get_completions(
vault: &Vault,
_files: &Box<[PathBuf]>,
_files: &[PathBuf],
params: &CompletionParams,
path: &Path,
_settings: &Settings
_settings: &Settings,
) -> Option<CompletionResponse> {

let parser = Parser::new(vault);
let querier = Querier::new(vault);
let location = Location {
path,
line: params.text_document_position.position.line as usize,
character: params.text_document_position.position.character as usize
character: params.text_document_position.position.character as usize,
};

dbg!(completions(&parser, &querier, location))
}

fn completions(parser: &Parser, querier: &Querier, location: Location) -> Option<CompletionResponse> {
let (file_ref, link_info) = parser.parse_link(location)?;
let files = querier.query(file_ref);
Some(to_completion_response(&link_info, files.into_par_iter()))
completions(&parser, &querier, location)
}

fn completions(
parser: &Parser,
querier: &Querier,
location: Location,
) -> Option<CompletionResponse> {
let (link, link_info) = parser.parse_link(location)?;

struct Parser<'a> {
vault: &'a Vault
}

impl<'a> Parser<'a> {
fn new(vault: &'a Vault) -> Self {
Self { vault }
}
}

impl Parser<'_> {
fn parse_link(&self, location: Location) -> Option<(FileRef, LinkInfo)> {

let chars = self.vault.select_line(location.path, location.line as isize)?;
let line_string = String::from_iter(chars);

let re = Regex::new(r"\[\[(?<file_ref>.*?)\]\]").expect("Regex failed to compile");

let c = re.captures_iter(&line_string)
.next()?;
let file_ref = c.name("file_ref")?.as_str();
let char_range = c.get(0)?.start()..c.get(0)?.end();

Some((file_ref.to_string(), LinkInfo {char_range, line: location.line}))
let named_entities = querier.query(link);

}
}

type FileRef = String;
struct LinkInfo {
line: usize,
char_range: Range<usize>
Some(to_completion_response(&link_info, named_entities))
}

struct Location<'fs> {
path: &'fs Path,
line: usize,
character: usize
}

struct Querier<'a> {
vault: &'a Vault
}

impl<'a> Querier<'a> {
fn new(vault: &'a Vault) -> Self {
Self { vault }
}
}

impl<'a> Querier<'a> {
fn query(&self, file_ref: FileRef) -> Vec<&'a Path> {
let paths = self.vault.md_files
.keys()
.map(|key| (key.file_name().unwrap().to_str().unwrap().to_string(), key))
.collect::<Vec<_>>();

let matched = fuzzy_match(&file_ref, paths);
matched.into_par_iter()
.map(|((_, path), _)| path as &Path)
.collect()
}
}

impl<'a> Matchable for (String, &'a PathBuf) {
fn match_string(&self) -> &str {
self.0.as_str()
}
}
character: usize,
}

fn to_completion_response(
info: &LinkInfo,
named_entities: impl IndexedParallelIterator<Item = NamedEntity>,
) -> CompletionResponse {
let items = named_entities
.enumerate()
.flat_map(|(i, entity)| {
let label = entity_to_label(&entity);

Some(CompletionItem {
label: label.clone(),
sort_text: Some(i.to_string()),
text_edit: Some(CompletionTextEdit::Edit(TextEdit {
range: tower_lsp::lsp_types::Range {
start: tower_lsp::lsp_types::Position {
line: info.line as u32,
character: info.char_range.start as u32,
},
end: tower_lsp::lsp_types::Position {
line: info.line as u32,
character: info.char_range.end as u32,
},
},
new_text: format!("[[{label}]]"),
})),
filter_text: Some(format!("[[{label}")),
..Default::default()
})
})
.collect::<Vec<_>>();

dbg!(&items);

pub trait Matchable {
fn match_string(&self) -> &str;
CompletionResponse::List(CompletionList {
is_incomplete: true,
items,
})
}


struct NucleoMatchable<T: Matchable>(T);
impl<T: Matchable> Deref for NucleoMatchable<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
fn named_entity_file_ref(entity: &NamedEntity) -> String {
entity.0.file_stem().unwrap().to_str().unwrap().to_string()
}

impl<T: Matchable> AsRef<str> for NucleoMatchable<T> {
fn as_ref(&self) -> &str {
self.match_string()
fn entity_to_label(entity: &NamedEntity) -> String {
let file_ref = named_entity_file_ref(entity); // TODO: abstract this better; there is possible duplication in querier
match entity {
NamedEntity(_, querier::NamedEntityInfo::File) => file_ref.to_string(),
NamedEntity(_, NamedEntityInfo::Heading(heading)) => format!("{file_ref}#{heading}"),
NamedEntity(_, NamedEntityInfo::IndexedBlock(index)) => format!("{file_ref}#^{index}"),
}
}


use nucleo_matcher::{pattern::{self, Normalization}, Matcher};
pub fn fuzzy_match<'a, T: Matchable>(
filter_text: &str,
items: impl IntoIterator<Item = T>,
) -> Vec<(T, u32)> {
let items = items.into_iter().map(NucleoMatchable);

let mut matcher = Matcher::new(nucleo_matcher::Config::DEFAULT);
let matches = pattern::Pattern::parse(
filter_text,
pattern::CaseMatching::Smart,
Normalization::Smart,
)
.match_list(items, &mut matcher);

matches
.into_iter()
.map(|(item, score)| (item.0, score))
.collect()
}




fn to_completion_response(info: &LinkInfo, files: impl IndexedParallelIterator<Item = &Path>) -> CompletionResponse {
let items = files.enumerate()
.flat_map(|(i, path)| Some((i, path.file_name()?.to_str()?)))
.flat_map(|(i, name)| Some(CompletionItem {
label: name.to_string(),
sort_text: Some(i.to_string()),
..Default::default()
}))
.collect::<Vec<_>>();

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

6 changes: 1 addition & 5 deletions crates/markdown-oxide/src/codeactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@ use tower_lsp::lsp_types::{
Position, Range, ResourceOp, TextDocumentEdit, TextEdit, Url, WorkspaceEdit,
};

use crate::{
daily::filename_is_formatted,
diagnostics::path_unresolved_references,

};
use crate::{daily::filename_is_formatted, diagnostics::path_unresolved_references};

use moxide_config::Settings;

Expand Down
2 changes: 1 addition & 1 deletion crates/markdown-oxide/src/commands.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use std::fs::File;
use std::path::Path;

use moxide_config::Settings;
use chrono::offset::Local;
use chrono::NaiveDateTime;
use fuzzydate::parse;
use moxide_config::Settings;
use serde_json::Value;
use tower_lsp::jsonrpc::{Error, Result};
use tower_lsp::lsp_types::{MessageType, ShowDocumentParams, Url};
Expand Down
4 changes: 1 addition & 3 deletions crates/markdown-oxide/src/hover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ use std::path::Path;

use tower_lsp::lsp_types::{Hover, HoverContents, HoverParams};

use crate::{
ui::{preview_reference, preview_referenceable},
};
use crate::ui::{preview_reference, preview_referenceable};

use vault::Vault;

Expand Down
9 changes: 6 additions & 3 deletions crates/markdown-oxide/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ use std::ops::{Deref, DerefMut};
use std::path::PathBuf;
use std::sync::Arc;

use moxide_config::Settings;
use diagnostics::diagnostics;
use itertools::Itertools;
use moxide_config::Settings;
use rayon::prelude::*;
use references::references;
use serde_json::Value;
Expand All @@ -19,7 +19,6 @@ use tower_lsp::lsp_types::*;
use tower_lsp::{Client, LanguageServer, LspService, Server};
use vault::Vault;


mod codeactions;
mod codelens;
mod commands;
Expand Down Expand Up @@ -554,7 +553,11 @@ impl LanguageServer for Backend {
}; // TODO: this is bad

let res = self
.bind_vault(|vault| Ok(completions::get_completions(vault, &files, &params, &path, &settings)))
.bind_vault(|vault| {
Ok(completions::get_completions(
vault, &files, &params, &path, &settings,
))
})
.await;

let elapsed = timer.elapsed();
Expand Down
4 changes: 1 addition & 3 deletions crates/markdown-oxide/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,7 @@ fn map_to_lsp_tree(tree: Vec<Node>) -> Vec<DocumentSymbol> {

#[cfg(test)]
mod test {
use crate::{
symbol,
};
use crate::symbol;

use vault::{HeadingLevel, MDHeading};

Expand Down
7 changes: 7 additions & 0 deletions crates/vault/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,13 @@ impl Vault {
.map(|slice| slice.chars().collect_vec())
}

pub fn select_line_str<'a>(&'a self, path: &Path, line: usize) -> Option<&'a str> {
let rope = self.ropes.get(path)?;

let line = rope.get_line(line)?;
line.as_str()
}

pub fn select_headings(&self, path: &Path) -> Option<&Vec<MDHeading>> {
let md_file = self.md_files.get(path)?;
let headings = &md_file.headings;
Expand Down
Loading

0 comments on commit 9db3944

Please sign in to comment.