mirror of
https://github.com/SpaceManiac/SpacemanDMM.git
synced 2025-12-23 05:36:47 +00:00
Replace PathBuf with Url where possible in the langserver
This commit is contained in:
parent
372e61d114
commit
83ca8323af
2 changed files with 98 additions and 71 deletions
|
|
@ -4,36 +4,34 @@
|
|||
|
||||
use std::io::{self, Read, BufRead};
|
||||
use std::borrow::Cow;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use url::Url;
|
||||
|
||||
use jsonrpc;
|
||||
use langserver::{TextDocumentItem, TextDocumentIdentifier,
|
||||
VersionedTextDocumentIdentifier, TextDocumentContentChangeEvent};
|
||||
|
||||
use super::{url_to_path, invalid_request};
|
||||
use super::{invalid_request};
|
||||
|
||||
/// A store for the contents of currently-open documents, with appropriate
|
||||
/// fallback for documents which are not currently open.
|
||||
#[derive(Default)]
|
||||
pub struct DocumentStore {
|
||||
map: HashMap<PathBuf, Document>,
|
||||
map: HashMap<Url, Document>,
|
||||
}
|
||||
|
||||
impl DocumentStore {
|
||||
pub fn open(&mut self, doc: TextDocumentItem) -> Result<(), jsonrpc::Error> {
|
||||
let path = url_to_path(doc.uri)?;
|
||||
match self.map.insert(path, Document::new(doc.version, doc.text)) {
|
||||
match self.map.insert(doc.uri, Document::new(doc.version, doc.text)) {
|
||||
None => Ok(()),
|
||||
Some(_) => Err(invalid_request("opened a document a second time")),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn close(&mut self, id: TextDocumentIdentifier) -> Result<PathBuf, jsonrpc::Error> {
|
||||
let path = url_to_path(id.uri)?;
|
||||
match self.map.remove(&path) {
|
||||
Some(_) => Ok(path),
|
||||
pub fn close(&mut self, id: TextDocumentIdentifier) -> Result<Url, jsonrpc::Error> {
|
||||
match self.map.remove(&id.uri) {
|
||||
Some(_) => Ok(id.uri),
|
||||
None => Err(invalid_request("cannot close non-opened document")),
|
||||
}
|
||||
}
|
||||
|
|
@ -42,9 +40,7 @@ impl DocumentStore {
|
|||
&mut self,
|
||||
doc_id: VersionedTextDocumentIdentifier,
|
||||
changes: Vec<TextDocumentContentChangeEvent>,
|
||||
) -> Result<PathBuf, jsonrpc::Error> {
|
||||
let path = url_to_path(doc_id.uri)?;
|
||||
|
||||
) -> Result<Url, jsonrpc::Error> {
|
||||
// "If a versioned text document identifier is sent from the server to
|
||||
// the client and the file is not open in the editor (the server has
|
||||
// not received an open notification before) the server can send `null`
|
||||
|
|
@ -55,7 +51,7 @@ impl DocumentStore {
|
|||
None => return Err(invalid_request("don't know how to deal with this")),
|
||||
};
|
||||
|
||||
let document = match self.map.get_mut(&path) {
|
||||
let document = match self.map.get_mut(&doc_id.uri) {
|
||||
Some(doc) => doc,
|
||||
None => return Err(invalid_request("that document isn't opened")),
|
||||
};
|
||||
|
|
@ -67,7 +63,7 @@ impl DocumentStore {
|
|||
document.version = new_version;
|
||||
|
||||
// Make an effort to apply all changes, even if one failed.
|
||||
let mut result = Ok(path);
|
||||
let mut result = Ok(doc_id.uri);
|
||||
for change in changes {
|
||||
if let Err(e) = document.change(change) {
|
||||
result = Err(e);
|
||||
|
|
@ -76,23 +72,34 @@ impl DocumentStore {
|
|||
result
|
||||
}
|
||||
|
||||
pub fn get_contents<'a>(&'a self, path: &Path) -> io::Result<Cow<'a, str>> {
|
||||
match self.map.get(path) {
|
||||
Some(document) => Ok(Cow::Borrowed(&document.text)),
|
||||
None => {
|
||||
let mut text = String::new();
|
||||
let mut file = ::std::fs::File::open(path)?;
|
||||
file.read_to_string(&mut text)?;
|
||||
Ok(Cow::Owned(text))
|
||||
}
|
||||
pub fn get_contents<'a>(&'a self, url: &Url) -> io::Result<Cow<'a, str>> {
|
||||
if let Some(document) = self.map.get(url) {
|
||||
return Ok(Cow::Borrowed(&document.text));
|
||||
}
|
||||
|
||||
if let Ok(path) = ::url_to_path(url) {
|
||||
let mut text = String::new();
|
||||
let mut file = ::std::fs::File::open(path)?;
|
||||
file.read_to_string(&mut text)?;
|
||||
return Ok(Cow::Owned(text));
|
||||
}
|
||||
|
||||
return Err(io::Error::new(io::ErrorKind::NotFound,
|
||||
format!("URL not opened and schema is not 'file': {}", url)));
|
||||
}
|
||||
|
||||
pub fn read(&self, path: &Path) -> io::Result<Box<io::Read>> {
|
||||
match self.map.get(path) {
|
||||
Some(document) => Ok(Box::new(Cursor::new(document.text.clone())) as Box<io::Read>),
|
||||
None => ::std::fs::File::open(path).map(|f| Box::new(f) as Box<io::Read>),
|
||||
pub fn read(&self, url: &Url) -> io::Result<Box<io::Read>> {
|
||||
if let Some(document) = self.map.get(url) {
|
||||
return Ok(Box::new(Cursor::new(document.text.clone())) as Box<io::Read>);
|
||||
}
|
||||
|
||||
if let Ok(path) = ::url_to_path(url) {
|
||||
let file = ::std::fs::File::open(path)?;
|
||||
return Ok(Box::new(file) as Box<io::Read>);
|
||||
}
|
||||
|
||||
return Err(io::Error::new(io::ErrorKind::NotFound,
|
||||
format!("URL not opened and schema is not 'file': {}", url)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ mod find_references;
|
|||
mod extras;
|
||||
mod completion;
|
||||
|
||||
use std::path::{PathBuf, Path};
|
||||
use std::path::PathBuf;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::rc::Rc;
|
||||
|
|
@ -121,14 +121,14 @@ struct Engine<'a, W: 'a> {
|
|||
|
||||
status: InitStatus,
|
||||
parent_pid: u64,
|
||||
root: PathBuf,
|
||||
root: Option<Url>,
|
||||
|
||||
context: &'a dm::Context,
|
||||
preprocessor: Option<dm::preprocessor::Preprocessor<'a>>,
|
||||
objtree: dm::objtree::ObjectTree,
|
||||
references_table: Option<find_references::ReferencesTable>,
|
||||
|
||||
annotations: HashMap<PathBuf, (FileId, FileId, Rc<AnnotationTree>)>,
|
||||
annotations: HashMap<Url, (FileId, FileId, Rc<AnnotationTree>)>,
|
||||
|
||||
client_caps: ClientCaps,
|
||||
}
|
||||
|
|
@ -141,7 +141,7 @@ impl<'a, W: io::ResponseWrite> Engine<'a, W> {
|
|||
|
||||
status: InitStatus::Starting,
|
||||
parent_pid: 0,
|
||||
root: Default::default(),
|
||||
root: None,
|
||||
|
||||
context,
|
||||
preprocessor: None,
|
||||
|
|
@ -191,14 +191,21 @@ impl<'a, W: io::ResponseWrite> Engine<'a, W> {
|
|||
}
|
||||
|
||||
fn file_url(&self, file: dm::FileId) -> Result<Url, jsonrpc::Error> {
|
||||
path_to_url(self.root.join(self.context.file_path(file)))
|
||||
if let Some(ref root) = self.root {
|
||||
root.join(&self.context.file_path(file).display().to_string())
|
||||
.map_err(|e| invalid_request(format!("error in file_url: {}", e)))
|
||||
} else {
|
||||
Err(invalid_request("cannot file_url without a root"))
|
||||
}
|
||||
}
|
||||
|
||||
fn location_link(&self, loc: dm::Location) -> String {
|
||||
fn location_link(&self, loc: dm::Location) -> Result<String, jsonrpc::Error> {
|
||||
if loc.file == dm::FileId::builtins() {
|
||||
String::new()
|
||||
Ok(String::new())
|
||||
} else {
|
||||
format!("file:{}#{}", self.root.join(self.context.file_path(loc.file)).display().to_string().replace("\\", "/"), loc.line)
|
||||
let mut url = self.file_url(loc.file)?;
|
||||
url.set_fragment(Some(&loc.line.to_string()));
|
||||
Ok(url.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -351,7 +358,7 @@ impl<'a, W: io::ResponseWrite> Engine<'a, W> {
|
|||
related_information,
|
||||
.. Default::default()
|
||||
};
|
||||
map.entry(self.context.file_path(loc.file))
|
||||
map.entry(self.file_url(loc.file)?)
|
||||
.or_insert_with(Default::default)
|
||||
.push(diag);
|
||||
|
||||
|
|
@ -365,18 +372,17 @@ impl<'a, W: io::ResponseWrite> Engine<'a, W> {
|
|||
source: error.component().name().map(ToOwned::to_owned),
|
||||
.. Default::default()
|
||||
};
|
||||
map.entry(self.context.file_path(note.location().file))
|
||||
map.entry(self.file_url(note.location().file)?)
|
||||
.or_insert_with(Default::default)
|
||||
.push(diag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (path, diagnostics) in map {
|
||||
let joined_path = self.root.join(path);
|
||||
for (url, diagnostics) in map {
|
||||
self.issue_notification::<langserver::notification::PublishDiagnostics>(
|
||||
langserver::PublishDiagnosticsParams {
|
||||
uri: path_to_url(joined_path)?,
|
||||
uri: url,
|
||||
diagnostics,
|
||||
},
|
||||
);
|
||||
|
|
@ -385,14 +391,21 @@ impl<'a, W: io::ResponseWrite> Engine<'a, W> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn get_annotations(&mut self, path: &Path) -> Result<(FileId, FileId, Rc<AnnotationTree>), jsonrpc::Error> {
|
||||
Ok(match self.annotations.entry(path.to_owned()) {
|
||||
fn get_annotations(&mut self, url: &Url) -> Result<(FileId, FileId, Rc<AnnotationTree>), jsonrpc::Error> {
|
||||
Ok(match self.annotations.entry(url.to_owned()) {
|
||||
Entry::Occupied(o) => o.get().clone(),
|
||||
Entry::Vacant(v) => {
|
||||
let stripped = match path.strip_prefix(&self.root) {
|
||||
let path = url_to_path(url)?;
|
||||
let root = match self.root {
|
||||
Some(ref root) => url_to_path(root)?,
|
||||
None => return Err(invalid_request(format!("no root"))),
|
||||
};
|
||||
|
||||
let stripped = match path.strip_prefix(&root) {
|
||||
Ok(path) => path,
|
||||
Err(_) => return Err(invalid_request(format!("outside workspace: {}", path.display()))),
|
||||
};
|
||||
|
||||
let preprocessor = match self.preprocessor {
|
||||
Some(ref pp) => pp,
|
||||
None => return Err(invalid_request("no preprocessor")),
|
||||
|
|
@ -401,7 +414,7 @@ impl<'a, W: io::ResponseWrite> Engine<'a, W> {
|
|||
Some(id) => (id, preprocessor.branch_at_file(id, &self.context)),
|
||||
None => (FileId::default(), preprocessor.branch(&self.context)),
|
||||
};
|
||||
let contents = self.docs.read(path).map_err(invalid_request)?;
|
||||
let contents = self.docs.read(url).map_err(invalid_request)?;
|
||||
let file_id = preprocessor.push_file(stripped.to_owned(), contents);
|
||||
preprocessor.enable_annotations();
|
||||
let mut annotations = AnnotationTree::default();
|
||||
|
|
@ -599,10 +612,17 @@ handle_method_call! {
|
|||
if let Some(id) = init.process_id {
|
||||
self.parent_pid = id;
|
||||
}
|
||||
if let Some(url) = init.root_uri {
|
||||
self.root = url_to_path(url)?;
|
||||
if let Some(mut url) = init.root_uri {
|
||||
if !url.path().ends_with("/") {
|
||||
let path = format!("{}/", url.path());
|
||||
url.set_path(&path);
|
||||
}
|
||||
self.root = Some(url);
|
||||
} else if let Some(path) = init.root_path {
|
||||
self.root = PathBuf::from(path);
|
||||
match Url::from_directory_path(path) {
|
||||
Ok(url) => self.root = Some(url),
|
||||
Err(()) => return Err(invalid_request("Root path does not correspond to a valid URL")),
|
||||
}
|
||||
} else {
|
||||
return Err(invalid_request("No root directory was specified. Use 'Open Folder' or equivalent to open your DM environment."))
|
||||
}
|
||||
|
|
@ -727,8 +747,7 @@ handle_method_call! {
|
|||
}
|
||||
|
||||
on HoverRequest(&mut self, params) {
|
||||
let path = url_to_path(params.text_document.uri)?;
|
||||
let (_, file_id, annotations) = self.get_annotations(&path)?;
|
||||
let (_, file_id, annotations) = self.get_annotations(¶ms.text_document.uri)?;
|
||||
let location = dm::Location {
|
||||
file: file_id,
|
||||
line: params.position.line as u32 + 1,
|
||||
|
|
@ -768,7 +787,7 @@ handle_method_call! {
|
|||
} else {
|
||||
¤t.path
|
||||
};
|
||||
infos.push_front(format!("[{}]({}){}", path, self.location_link(var.value.location), constant));
|
||||
infos.push_front(format!("[{}]({}){}", path, self.location_link(var.value.location)?, constant));
|
||||
if let Some(ref decl) = var.declaration {
|
||||
let mut declaration = String::new();
|
||||
declaration.push_str("var");
|
||||
|
|
@ -822,7 +841,7 @@ handle_method_call! {
|
|||
¤t.path
|
||||
};
|
||||
let proc_value = proc.value.last().unwrap();
|
||||
let mut message = format!("[{}]({}) \n{}(", path, self.location_link(proc_value.location), last);
|
||||
let mut message = format!("[{}]({}) \n{}(", path, self.location_link(proc_value.location)?, last);
|
||||
let mut first = true;
|
||||
for each in proc_value.parameters.iter() {
|
||||
use std::fmt::Write;
|
||||
|
|
@ -864,8 +883,7 @@ handle_method_call! {
|
|||
}
|
||||
|
||||
on GotoDefinition(&mut self, params) {
|
||||
let path = url_to_path(params.text_document.uri)?;
|
||||
let (real_file_id, file_id, annotations) = self.get_annotations(&path)?;
|
||||
let (real_file_id, file_id, annotations) = self.get_annotations(¶ms.text_document.uri)?;
|
||||
let location = dm::Location {
|
||||
file: file_id,
|
||||
line: params.position.line as u32 + 1,
|
||||
|
|
@ -977,8 +995,7 @@ handle_method_call! {
|
|||
|
||||
on GotoTypeDefinition(&mut self, params) {
|
||||
// Like GotoDefinition, but only supports vars, then finds their types
|
||||
let path = url_to_path(params.text_document.uri)?;
|
||||
let (_, file_id, annotations) = self.get_annotations(&path)?;
|
||||
let (_, file_id, annotations) = self.get_annotations(¶ms.text_document.uri)?;
|
||||
let location = dm::Location {
|
||||
file: file_id,
|
||||
line: params.position.line as u32 + 1,
|
||||
|
|
@ -1032,8 +1049,7 @@ handle_method_call! {
|
|||
|
||||
on References(&mut self, params) {
|
||||
// Like GotoDefinition, but looks up references instead
|
||||
let path = url_to_path(params.text_document.uri)?;
|
||||
let (_, file_id, annotations) = self.get_annotations(&path)?;
|
||||
let (_, file_id, annotations) = self.get_annotations(¶ms.text_document.uri)?;
|
||||
let location = dm::Location {
|
||||
file: file_id,
|
||||
line: params.position.line as u32 + 1,
|
||||
|
|
@ -1171,8 +1187,7 @@ handle_method_call! {
|
|||
}
|
||||
|
||||
on Completion(&mut self, params) {
|
||||
let path = url_to_path(params.text_document.uri)?;
|
||||
let (_, file_id, annotations) = self.get_annotations(&path)?;
|
||||
let (_, file_id, annotations) = self.get_annotations(¶ms.text_document.uri)?;
|
||||
let location = dm::Location {
|
||||
file: file_id,
|
||||
line: params.position.line as u32 + 1,
|
||||
|
|
@ -1248,8 +1263,7 @@ handle_method_call! {
|
|||
}
|
||||
|
||||
on SignatureHelpRequest(&mut self, params) {
|
||||
let path = url_to_path(params.text_document.uri)?;
|
||||
let (_, file_id, annotations) = self.get_annotations(&path)?;
|
||||
let (_, file_id, annotations) = self.get_annotations(¶ms.text_document.uri)?;
|
||||
let location = dm::Location {
|
||||
file: file_id,
|
||||
line: params.position.line as u32 + 1,
|
||||
|
|
@ -1422,8 +1436,7 @@ handle_method_call! {
|
|||
}
|
||||
|
||||
// root
|
||||
let path = url_to_path(params.text_document.uri)?;
|
||||
let (_, file_id, annotations) = self.get_annotations(&path)?;
|
||||
let (_, file_id, annotations) = self.get_annotations(¶ms.text_document.uri)?;
|
||||
if annotations.is_empty() {
|
||||
None
|
||||
} else {
|
||||
|
|
@ -1443,9 +1456,16 @@ handle_notification! {
|
|||
}
|
||||
|
||||
on Initialized(&mut self, _) {
|
||||
eprintln!("workspace root: {}", self.root.display());
|
||||
let environment = dm::detect_environment(&self.root, dm::DEFAULT_ENV)
|
||||
.map_err(invalid_request)?;
|
||||
eprintln!("workspace root: {:?}", self.root);
|
||||
|
||||
let mut environment = None;
|
||||
if let Some(ref root) = self.root {
|
||||
// TODO: support non-files here
|
||||
if let Ok(root_path) = url_to_path(root) {
|
||||
environment = dm::detect_environment(&root_path, dm::DEFAULT_ENV).map_err(invalid_request)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(environment) = environment {
|
||||
self.parse_environment(environment)?;
|
||||
} else {
|
||||
|
|
@ -1460,13 +1480,13 @@ handle_notification! {
|
|||
}
|
||||
|
||||
on DidCloseTextDocument(&mut self, params) {
|
||||
let path = self.docs.close(params.text_document)?;
|
||||
self.annotations.remove(&path);
|
||||
let url = self.docs.close(params.text_document)?;
|
||||
self.annotations.remove(&url);
|
||||
}
|
||||
|
||||
on DidChangeTextDocument(&mut self, params) {
|
||||
let path = self.docs.change(params.text_document, params.content_changes)?;
|
||||
self.annotations.remove(&path);
|
||||
let url = self.docs.change(params.text_document, params.content_changes)?;
|
||||
self.annotations.remove(&url);
|
||||
}
|
||||
|
||||
on DidChangeConfiguration(&mut self, _) {}
|
||||
|
|
@ -1500,7 +1520,7 @@ fn invalid_request<S: ToString>(message: S) -> jsonrpc::Error {
|
|||
}
|
||||
}
|
||||
|
||||
fn url_to_path(url: Url) -> Result<PathBuf, jsonrpc::Error> {
|
||||
fn url_to_path(url: &Url) -> Result<PathBuf, jsonrpc::Error> {
|
||||
if url.scheme() != "file" {
|
||||
return Err(invalid_request("URI must have 'file' scheme"));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue