use crossbeam_channel::{after, select, Receiver};
use lsp_server::{Connection, Message, Notification, Request};
use lsp_types::{
- notification::{DidOpenTextDocument, Exit},
- request::Shutdown,
- ClientCapabilities, DidOpenTextDocumentParams, GotoCapability, TextDocumentClientCapabilities,
- TextDocumentIdentifier, TextDocumentItem, Url, WorkDoneProgress,
+ notification::Exit, request::Shutdown, TextDocumentIdentifier, Url, WorkDoneProgress,
};
+use lsp_types::{ProgressParams, ProgressParamsValue};
use serde::Serialize;
use serde_json::{to_string_pretty, Value};
use tempfile::TempDir;
-use test_utils::{find_mismatch, parse_fixture};
+use test_utils::{find_mismatch, Fixture};
-use req::{ProgressParams, ProgressParamsValue};
-use rust_analyzer::{main_loop, req, ServerConfig};
+use ra_db::AbsPathBuf;
+use ra_project_model::ProjectManifest;
+use rust_analyzer::{
+ config::{ClientCapsConfig, Config, FilesConfig, FilesWatcher, LinkedProject},
+ main_loop,
+};
pub struct Project<'a> {
fixture: &'a str,
with_sysroot: bool,
tmp_dir: Option<TempDir>,
roots: Vec<PathBuf>,
+ config: Option<Box<dyn Fn(&mut Config)>>,
}
impl<'a> Project<'a> {
pub fn with_fixture(fixture: &str) -> Project {
- Project { fixture, tmp_dir: None, roots: vec![], with_sysroot: false }
+ Project { fixture, tmp_dir: None, roots: vec![], with_sysroot: false, config: None }
}
pub fn tmp_dir(mut self, tmp_dir: TempDir) -> Project<'a> {
self
}
- pub fn root(mut self, path: &str) -> Project<'a> {
+ pub(crate) fn root(mut self, path: &str) -> Project<'a> {
self.roots.push(path.into());
self
}
self
}
+ pub fn with_config(mut self, config: impl Fn(&mut Config) + 'static) -> Project<'a> {
+ self.config = Some(Box::new(config));
+ self
+ }
+
pub fn server(self) -> Server {
let tmp_dir = self.tmp_dir.unwrap_or_else(|| TempDir::new().unwrap());
static INIT: Once = Once::new();
INIT.call_once(|| {
env_logger::builder().is_test(true).try_init().unwrap();
- ra_prof::set_filter(if crate::PROFILE.is_empty() {
- ra_prof::Filter::disabled()
- } else {
- ra_prof::Filter::from_spec(&crate::PROFILE)
- });
+ ra_prof::init_from(crate::PROFILE);
});
- let mut paths = vec![];
-
- for entry in parse_fixture(self.fixture) {
- let path = tmp_dir.path().join(entry.meta);
+ for entry in Fixture::parse(self.fixture) {
+ let path = tmp_dir.path().join(&entry.path['/'.len_utf8()..]);
fs::create_dir_all(path.parent().unwrap()).unwrap();
fs::write(path.as_path(), entry.text.as_bytes()).unwrap();
- paths.push((path, entry.text));
}
- let roots = self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect();
+ let tmp_dir_path = AbsPathBuf::assert(tmp_dir.path().to_path_buf());
+ let mut roots =
+ self.roots.into_iter().map(|root| tmp_dir_path.join(root)).collect::<Vec<_>>();
+ if roots.is_empty() {
+ roots.push(tmp_dir_path.clone());
+ }
+ let linked_projects = roots
+ .into_iter()
+ .map(|it| ProjectManifest::discover_single(&it).unwrap())
+ .map(LinkedProject::from)
+ .collect::<Vec<_>>();
- Server::new(tmp_dir, self.with_sysroot, roots, paths)
+ let mut config = Config {
+ client_caps: ClientCapsConfig {
+ location_link: true,
+ code_action_literals: true,
+ work_done_progress: true,
+ ..Default::default()
+ },
+ with_sysroot: self.with_sysroot,
+ linked_projects,
+ files: FilesConfig { watcher: FilesWatcher::Client, exclude: Vec::new() },
+ ..Config::new(tmp_dir_path)
+ };
+ if let Some(f) = &self.config {
+ f(&mut config)
+ }
+
+ Server::new(tmp_dir, config)
}
}
}
impl Server {
- fn new(
- dir: TempDir,
- with_sysroot: bool,
- roots: Vec<PathBuf>,
- files: Vec<(PathBuf, String)>,
- ) -> Server {
- let path = dir.path().to_path_buf();
-
- let roots = if roots.is_empty() { vec![path] } else { roots };
+ fn new(dir: TempDir, config: Config) -> Server {
let (connection, client) = Connection::memory();
let _thread = jod_thread::Builder::new()
.name("test server".to_string())
- .spawn(move || {
- main_loop(
- roots,
- ClientCapabilities {
- workspace: None,
- text_document: Some(TextDocumentClientCapabilities {
- definition: Some(GotoCapability {
- dynamic_registration: None,
- link_support: Some(true),
- }),
- ..Default::default()
- }),
- window: None,
- experimental: None,
- },
- ServerConfig { with_sysroot, ..ServerConfig::default() },
- connection,
- )
- .unwrap()
- })
+ .spawn(move || main_loop(config, connection).unwrap())
.expect("failed to spawn a thread");
- let res =
- Server { req_id: Cell::new(1), dir, messages: Default::default(), client, _thread };
-
- for (path, text) in files {
- res.notification::<DidOpenTextDocument>(DidOpenTextDocumentParams {
- text_document: TextDocumentItem {
- uri: Url::from_file_path(path).unwrap(),
- language_id: "rust".to_string(),
- version: 0,
- text,
- },
- })
- }
- res
+ Server { req_id: Cell::new(1), dir, messages: Default::default(), client, _thread }
}
pub fn doc_id(&self, rel_path: &str) -> TextDocumentIdentifier {
self.client.sender.send(r.into()).unwrap();
while let Some(msg) = self.recv() {
match msg {
- Message::Request(req) => panic!("unexpected request: {:?}", req),
+ Message::Request(req) => {
+ if req.method != "window/workDoneProgress/create"
+ && !(req.method == "client/registerCapability"
+ && req.params.to_string().contains("workspace/didChangeWatchedFiles"))
+ {
+ panic!("unexpected request: {:?}", req)
+ }
+ }
Message::Notification(_) => (),
Message::Response(res) => {
assert_eq!(res.id, id);
Message::Notification(n) if n.method == "$/progress" => {
match n.clone().extract::<ProgressParams>("$/progress").unwrap() {
ProgressParams {
- token: req::ProgressToken::String(ref token),
+ token: lsp_types::ProgressToken::String(ref token),
value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(_)),
- } if token == "rustAnalyzer/startup" => true,
+<<<<<<< HEAD
+ } if token == "rustAnalyzer/roots scanned" => true,
+=======
+ } if token == "rustAnalyzer/rootsScanned" => true,
+>>>>>>> Veetaha-feat/sync-branch
_ => false,
}
}