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::Token;
|
||||||
use crate::parser::source::VirtualSource;
|
use crate::parser::source::VirtualSource;
|
||||||
use crate::parser::util;
|
use crate::parser::util;
|
||||||
|
use lsp::conceal::Conceals;
|
||||||
use lsp::hints::Hints;
|
use lsp::hints::Hints;
|
||||||
use parser::util::escape_source;
|
use parser::util::escape_source;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
@ -330,6 +331,7 @@ impl Rule for ListRule {
|
||||||
offset.unwrap_or(usize::MAX),
|
offset.unwrap_or(usize::MAX),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Semantic
|
||||||
if let Some((sems, tokens)) =
|
if let Some((sems, tokens)) =
|
||||||
Semantics::from_source(cursor.source.clone(), &state.shared.lsp)
|
Semantics::from_source(cursor.source.clone(), &state.shared.lsp)
|
||||||
{
|
{
|
||||||
|
@ -340,12 +342,30 @@ impl Rule for ListRule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(hints) =
|
if let Some(conceals) =
|
||||||
Hints::from_source(cursor.source.clone(), &state.shared.lsp)
|
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();
|
let mut label = String::new();
|
||||||
for (_, id) in &depth
|
for (_, id) in &depth {
|
||||||
{
|
|
||||||
if !label.is_empty() {
|
if !label.is_empty() {
|
||||||
label.push('.');
|
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 crate::parser::source::Source;
|
||||||
|
|
||||||
|
use super::conceal::ConcealsData;
|
||||||
use super::definition::DefinitionData;
|
use super::definition::DefinitionData;
|
||||||
use super::hints::HintsData;
|
use super::hints::HintsData;
|
||||||
use super::semantic::SemanticsData;
|
use super::semantic::SemanticsData;
|
||||||
|
@ -14,6 +15,7 @@ pub struct LSPData {
|
||||||
pub semantic_data: HashMap<Rc<dyn Source>, SemanticsData>,
|
pub semantic_data: HashMap<Rc<dyn Source>, SemanticsData>,
|
||||||
pub inlay_hints: HashMap<Rc<dyn Source>, HintsData>,
|
pub inlay_hints: HashMap<Rc<dyn Source>, HintsData>,
|
||||||
pub definitions: HashMap<Rc<dyn Source>, DefinitionData>,
|
pub definitions: HashMap<Rc<dyn Source>, DefinitionData>,
|
||||||
|
pub conceals: HashMap<Rc<dyn Source>, ConcealsData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LSPData {
|
impl LSPData {
|
||||||
|
@ -23,6 +25,7 @@ impl LSPData {
|
||||||
semantic_data: HashMap::new(),
|
semantic_data: HashMap::new(),
|
||||||
inlay_hints: HashMap::new(),
|
inlay_hints: HashMap::new(),
|
||||||
definitions: HashMap::new(),
|
definitions: HashMap::new(),
|
||||||
|
conceals: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,5 +43,9 @@ impl LSPData {
|
||||||
self.definitions
|
self.definitions
|
||||||
.insert(source.clone(), DefinitionData::new());
|
.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)]
|
#[derive(Debug)]
|
||||||
pub struct Hints<'a> {
|
pub struct Hints<'a> {
|
||||||
pub(self) hints: Ref<'a, HintsData>,
|
pub(self) hints: Ref<'a, HintsData>,
|
||||||
|
|
|
@ -2,3 +2,4 @@ pub mod data;
|
||||||
pub mod definition;
|
pub mod definition;
|
||||||
pub mod hints;
|
pub mod hints;
|
||||||
pub mod semantic;
|
pub mod semantic;
|
||||||
|
pub mod conceal;
|
||||||
|
|
|
@ -305,6 +305,7 @@ impl SemanticsData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Temporary data returned by [`Self::from_source_impl`]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Semantics<'a> {
|
pub struct Semantics<'a> {
|
||||||
pub(self) sems: Ref<'a, SemanticsData>,
|
pub(self) sems: Ref<'a, SemanticsData>,
|
||||||
|
|
|
@ -9,12 +9,17 @@ mod parser;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use dashmap::DashMap;
|
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::langparser::LangParser;
|
||||||
use parser::parser::ParseMode;
|
use parser::parser::ParseMode;
|
||||||
use parser::parser::Parser;
|
use parser::parser::Parser;
|
||||||
use parser::parser::ParserState;
|
use parser::parser::ParserState;
|
||||||
use parser::reports::Report;
|
use parser::reports::Report;
|
||||||
use parser::source::SourceFile;
|
use parser::source::SourceFile;
|
||||||
|
use tower_lsp::jsonrpc;
|
||||||
use tower_lsp::lsp_types::*;
|
use tower_lsp::lsp_types::*;
|
||||||
use tower_lsp::Client;
|
use tower_lsp::Client;
|
||||||
use tower_lsp::LanguageServer;
|
use tower_lsp::LanguageServer;
|
||||||
|
@ -29,6 +34,7 @@ struct Backend {
|
||||||
semantic_token_map: DashMap<String, Vec<SemanticToken>>,
|
semantic_token_map: DashMap<String, Vec<SemanticToken>>,
|
||||||
diagnostic_map: DashMap<String, Vec<Diagnostic>>,
|
diagnostic_map: DashMap<String, Vec<Diagnostic>>,
|
||||||
hints_map: DashMap<String, Vec<InlayHint>>,
|
hints_map: DashMap<String, Vec<InlayHint>>,
|
||||||
|
conceals_map: DashMap<String, Vec<ConcealInfo>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[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 stdin = tokio::io::stdin();
|
||||||
let stdout = tokio::io::stdout();
|
let stdout = tokio::io::stdout();
|
||||||
|
|
||||||
let (service, socket) = LspService::new(|client| Backend {
|
let (service, socket) = LspService::build(|client| Backend {
|
||||||
client,
|
client,
|
||||||
document_map: DashMap::new(),
|
document_map: DashMap::new(),
|
||||||
definition_map: DashMap::new(),
|
definition_map: DashMap::new(),
|
||||||
semantic_token_map: DashMap::new(),
|
semantic_token_map: DashMap::new(),
|
||||||
diagnostic_map: DashMap::new(),
|
diagnostic_map: DashMap::new(),
|
||||||
hints_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;
|
Server::new(stdin, stdout, socket).serve(service).await;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue