Add conceal support
This commit is contained in:
parent
de983979c4
commit
0fddb56b03
7 changed files with 214 additions and 6 deletions
|
@ -23,6 +23,7 @@ use crate::parser::source::Cursor;
|
|||
use crate::parser::source::Token;
|
||||
use crate::parser::source::VirtualSource;
|
||||
use crate::parser::util;
|
||||
use lsp::conceal::Conceals;
|
||||
use lsp::hints::Hints;
|
||||
use parser::util::escape_source;
|
||||
use regex::Regex;
|
||||
|
@ -330,6 +331,7 @@ impl Rule for ListRule {
|
|||
offset.unwrap_or(usize::MAX),
|
||||
);
|
||||
|
||||
// Semantic
|
||||
if let Some((sems, tokens)) =
|
||||
Semantics::from_source(cursor.source.clone(), &state.shared.lsp)
|
||||
{
|
||||
|
@ -340,12 +342,30 @@ impl Rule for ListRule {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(hints) =
|
||||
Hints::from_source(cursor.source.clone(), &state.shared.lsp)
|
||||
if let Some(conceals) =
|
||||
Conceals::from_source(cursor.source.clone(), &state.shared.lsp)
|
||||
{
|
||||
let mut i = captures.get(1).unwrap().start();
|
||||
for (numbered, _) in &depth {
|
||||
conceals.add(
|
||||
i..i + 1,
|
||||
lsp::conceal::ConcealTarget::Highlight {
|
||||
text: if *numbered {
|
||||
"⦾".into()
|
||||
} else {
|
||||
"⦿".into()
|
||||
},
|
||||
highlight_group: "Function".into(),
|
||||
},
|
||||
);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Hints
|
||||
if let Some(hints) = Hints::from_source(cursor.source.clone(), &state.shared.lsp) {
|
||||
let mut label = String::new();
|
||||
for (_, id) in &depth
|
||||
{
|
||||
for (_, id) in &depth {
|
||||
if !label.is_empty() {
|
||||
label.push('.');
|
||||
}
|
||||
|
|
138
src/lsp/conceal.rs
Normal file
138
src/lsp/conceal.rs
Normal file
|
@ -0,0 +1,138 @@
|
|||
use std::cell::Ref;
|
||||
use std::cell::RefCell;
|
||||
use std::ops::Range;
|
||||
use std::rc::Rc;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use tower_lsp::jsonrpc::Request;
|
||||
use tower_lsp::jsonrpc::{self};
|
||||
use tower_lsp::lsp_types::Position;
|
||||
|
||||
use crate::parser::source::LineCursor;
|
||||
use crate::parser::source::Source;
|
||||
use crate::parser::source::SourceFile;
|
||||
use crate::parser::source::SourcePosition;
|
||||
use crate::parser::source::VirtualSource;
|
||||
|
||||
use super::data::LSPData;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ConcealParams {
|
||||
pub text_document: tower_lsp::lsp_types::TextDocumentIdentifier,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ConcealInfo {
|
||||
pub range: tower_lsp::lsp_types::Range,
|
||||
pub conceal_text: ConcealTarget,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub enum ConcealTarget {
|
||||
Text(String),
|
||||
Highlight {
|
||||
text: String,
|
||||
highlight_group: String,
|
||||
},
|
||||
}
|
||||
|
||||
/// Per file conceals
|
||||
#[derive(Debug)]
|
||||
pub struct ConcealsData {
|
||||
/// The current cursor
|
||||
cursor: RefCell<LineCursor>,
|
||||
|
||||
/// The conceals
|
||||
pub conceals: RefCell<Vec<ConcealInfo>>,
|
||||
}
|
||||
|
||||
impl ConcealsData {
|
||||
pub fn new(source: Rc<dyn Source>) -> Self {
|
||||
Self {
|
||||
cursor: RefCell::new(LineCursor::new(source)),
|
||||
conceals: RefCell::new(vec![]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Temporary data returned by [`Self::from_source_impl`]
|
||||
#[derive(Debug)]
|
||||
pub struct Conceals<'a> {
|
||||
pub(self) conceals: Ref<'a, ConcealsData>,
|
||||
// The source used when resolving the parent source
|
||||
pub(self) original_source: Rc<dyn Source>,
|
||||
/// The resolved parent source
|
||||
pub(self) source: Rc<dyn Source>,
|
||||
}
|
||||
|
||||
impl<'a> Conceals<'a> {
|
||||
fn from_source_impl(
|
||||
source: Rc<dyn Source>,
|
||||
lsp: &'a Option<RefCell<LSPData>>,
|
||||
original_source: Rc<dyn Source>,
|
||||
) -> Option<Self> {
|
||||
if (source.name().starts_with(":LUA:") || source.name().starts_with(":VAR:"))
|
||||
&& source.downcast_ref::<VirtualSource>().is_some()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(location) = source
|
||||
.clone()
|
||||
.downcast_rc::<VirtualSource>()
|
||||
.ok()
|
||||
.as_ref()
|
||||
.map(|parent| parent.location())
|
||||
.unwrap_or(None)
|
||||
{
|
||||
return Self::from_source_impl(location.source(), lsp, original_source);
|
||||
} else if let Ok(source) = source.clone().downcast_rc::<SourceFile>() {
|
||||
return Ref::filter_map(lsp.as_ref().unwrap().borrow(), |lsp: &LSPData| {
|
||||
lsp.conceals.get(&(source.clone() as Rc<dyn Source>))
|
||||
})
|
||||
.ok()
|
||||
.map(|conceals| Self {
|
||||
conceals,
|
||||
source,
|
||||
original_source,
|
||||
});
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn from_source(source: Rc<dyn Source>, lsp: &'a Option<RefCell<LSPData>>) -> Option<Self> {
|
||||
if lsp.is_none() {
|
||||
return None;
|
||||
}
|
||||
Self::from_source_impl(source.clone(), lsp, source)
|
||||
}
|
||||
|
||||
pub fn add(&self, range: Range<usize>, text: ConcealTarget) {
|
||||
let range = self.original_source.original_range(range).1;
|
||||
let mut cursor = self.conceals.cursor.borrow_mut();
|
||||
cursor.move_to(range.start);
|
||||
|
||||
let line = cursor.line;
|
||||
let start_char = cursor.line_pos;
|
||||
|
||||
cursor.move_to(range.end);
|
||||
assert_eq!(line, cursor.line);
|
||||
let end_char = cursor.line_pos;
|
||||
|
||||
self.conceals.conceals.borrow_mut().push(ConcealInfo {
|
||||
range: tower_lsp::lsp_types::Range {
|
||||
start: Position {
|
||||
line: line as u32,
|
||||
character: start_char as u32,
|
||||
},
|
||||
end: Position {
|
||||
line: line as u32,
|
||||
character: end_char as u32,
|
||||
},
|
||||
},
|
||||
conceal_text: text,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ use std::rc::Rc;
|
|||
|
||||
use crate::parser::source::Source;
|
||||
|
||||
use super::conceal::ConcealsData;
|
||||
use super::definition::DefinitionData;
|
||||
use super::hints::HintsData;
|
||||
use super::semantic::SemanticsData;
|
||||
|
@ -14,6 +15,7 @@ pub struct LSPData {
|
|||
pub semantic_data: HashMap<Rc<dyn Source>, SemanticsData>,
|
||||
pub inlay_hints: HashMap<Rc<dyn Source>, HintsData>,
|
||||
pub definitions: HashMap<Rc<dyn Source>, DefinitionData>,
|
||||
pub conceals: HashMap<Rc<dyn Source>, ConcealsData>,
|
||||
}
|
||||
|
||||
impl LSPData {
|
||||
|
@ -23,6 +25,7 @@ impl LSPData {
|
|||
semantic_data: HashMap::new(),
|
||||
inlay_hints: HashMap::new(),
|
||||
definitions: HashMap::new(),
|
||||
conceals: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,5 +43,9 @@ impl LSPData {
|
|||
self.definitions
|
||||
.insert(source.clone(), DefinitionData::new());
|
||||
}
|
||||
if !self.conceals.contains_key(&source) {
|
||||
self.conceals
|
||||
.insert(source.clone(), ConcealsData::new(source.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ impl HintsData {
|
|||
}
|
||||
}
|
||||
|
||||
/// Temporary data returned by [`Self::from_source_impl`]
|
||||
#[derive(Debug)]
|
||||
pub struct Hints<'a> {
|
||||
pub(self) hints: Ref<'a, HintsData>,
|
||||
|
|
|
@ -2,3 +2,4 @@ pub mod data;
|
|||
pub mod definition;
|
||||
pub mod hints;
|
||||
pub mod semantic;
|
||||
pub mod conceal;
|
||||
|
|
|
@ -305,6 +305,7 @@ impl SemanticsData {
|
|||
}
|
||||
}
|
||||
|
||||
/// Temporary data returned by [`Self::from_source_impl`]
|
||||
#[derive(Debug)]
|
||||
pub struct Semantics<'a> {
|
||||
pub(self) sems: Ref<'a, SemanticsData>,
|
||||
|
|
|
@ -9,12 +9,17 @@ mod parser;
|
|||
use std::rc::Rc;
|
||||
|
||||
use dashmap::DashMap;
|
||||
use downcast_rs::Downcast;
|
||||
use lsp::conceal::ConcealInfo;
|
||||
use lsp::conceal::ConcealParams;
|
||||
use lsp::conceal::ConcealTarget;
|
||||
use parser::langparser::LangParser;
|
||||
use parser::parser::ParseMode;
|
||||
use parser::parser::Parser;
|
||||
use parser::parser::ParserState;
|
||||
use parser::reports::Report;
|
||||
use parser::source::SourceFile;
|
||||
use tower_lsp::jsonrpc;
|
||||
use tower_lsp::lsp_types::*;
|
||||
use tower_lsp::Client;
|
||||
use tower_lsp::LanguageServer;
|
||||
|
@ -29,6 +34,7 @@ struct Backend {
|
|||
semantic_token_map: DashMap<String, Vec<SemanticToken>>,
|
||||
diagnostic_map: DashMap<String, Vec<Diagnostic>>,
|
||||
hints_map: DashMap<String, Vec<InlayHint>>,
|
||||
conceals_map: DashMap<String, Vec<ConcealInfo>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -112,6 +118,36 @@ impl Backend {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Conceals
|
||||
if let Some(lsp) = state.shared.lsp.as_ref() {
|
||||
let borrow = lsp.borrow();
|
||||
for (source, conceals) in &borrow.conceals {
|
||||
if let Some(path) = source
|
||||
.clone()
|
||||
.downcast_rc::<SourceFile>()
|
||||
.ok()
|
||||
.map(|source| source.path().to_owned())
|
||||
{
|
||||
self.conceals_map
|
||||
.insert(path, conceals.conceals.replace(vec![]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_conceal_request(
|
||||
&self,
|
||||
params: ConcealParams,
|
||||
) -> jsonrpc::Result<Vec<ConcealInfo>> {
|
||||
eprintln!("HERE {:#?}", self.conceals_map);
|
||||
if let Some(conceals) = self.conceals_map.get(params.text_document.uri.as_str()) {
|
||||
let (_, data) = conceals.pair();
|
||||
|
||||
eprintln!("HERE2");
|
||||
return Ok(data.to_vec());
|
||||
}
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -313,13 +349,17 @@ async fn main() {
|
|||
let stdin = tokio::io::stdin();
|
||||
let stdout = tokio::io::stdout();
|
||||
|
||||
let (service, socket) = LspService::new(|client| Backend {
|
||||
let (service, socket) = LspService::build(|client| Backend {
|
||||
client,
|
||||
document_map: DashMap::new(),
|
||||
definition_map: DashMap::new(),
|
||||
semantic_token_map: DashMap::new(),
|
||||
diagnostic_map: DashMap::new(),
|
||||
hints_map: DashMap::new(),
|
||||
});
|
||||
conceals_map: DashMap::new(),
|
||||
})
|
||||
.custom_method("textDocument/conceal", Backend::handle_conceal_request)
|
||||
.finish();
|
||||
|
||||
Server::new(stdin, stdout, socket).serve(service).await;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue