Skip to content

Commit

Permalink
Merge pull request #98 from dfinance/remove-event-handling
Browse files Browse the repository at this point in the history
"Remove event" handling
  • Loading branch information
mkurnikov authored Jul 5, 2020
2 parents f9fcabe + 2223059 commit aee35f1
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 35 deletions.
4 changes: 3 additions & 1 deletion crates/dialects/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ pub trait Dialect {
file: MoveFile,
deps: &[MoveFile],
sender: ProvidedAccountAddress,
) -> Result<(CompiledScript, Vec<CompiledModule>), ExecCompilerError> {
) -> Result<(Option<CompiledScript>, Vec<CompiledModule>), ExecCompilerError> {
let (mut script_defs, modules_defs, project_offsets_map) =
self.parse_files(file, deps, &sender)?;
script_defs.extend(modules_defs);
Expand All @@ -149,6 +149,8 @@ pub trait Dialect {

let (compiled_script, compiled_modules) =
self.check_and_generate_bytecode(script, deps, sender.clone())?;
let compiled_script =
compiled_script.expect("compile_and_run should always be called with the script");

let network_state = prepare_fake_network_state(compiled_modules, genesis_write_set);

Expand Down
26 changes: 12 additions & 14 deletions crates/dialects/src/lang/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,18 @@ pub fn vm_status_into_exec_status(vm_status: VMStatus) -> ExecutionError {

pub fn generate_bytecode(
program: PreBytecodeProgram,
) -> Result<(CompiledScript, Vec<CompiledModule>), Vec<Error>> {
let mut units = to_bytecode::translate::program(program)?;
let script = match units.remove(units.len() - 1) {
CompiledUnit::Script { script, .. } => script,
CompiledUnit::Module { .. } => unreachable!(),
};
let modules = units
.into_iter()
.map(|unit| match unit {
CompiledUnit::Module { module, .. } => module,
CompiledUnit::Script { .. } => unreachable!(),
})
.collect();
Ok((script, modules))
) -> Result<(Option<CompiledScript>, Vec<CompiledModule>), Vec<Error>> {
let units = to_bytecode::translate::program(program)?;

let mut gen_script = None;
let mut gen_modules = vec![];
for unit in units {
match unit {
CompiledUnit::Module { module, .. } => gen_modules.push(module),
CompiledUnit::Script { script, .. } => gen_script = Some(script),
}
}
Ok((gen_script, gen_modules))
}

pub fn serialize_script(script: CompiledScript) -> Result<Vec<u8>> {
Expand Down
64 changes: 54 additions & 10 deletions crates/integration_tests/tests/test_lsp.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
use lsp_server::{Connection, Message, Notification, Request, RequestId, Response};
use lsp_types::request::{Initialize, Shutdown};
use lsp_types::{
ClientCapabilities, DidChangeConfigurationParams, InitializeParams, InitializedParams,
ClientCapabilities, DidChangeConfigurationParams, DidChangeWatchedFilesParams,
FileChangeType, FileEvent, InitializeParams, InitializedParams, Url,
};

use analysis::config::Config;

use dialects::DialectName;

use integration_tests::config;
use integration_tests::{config, get_script_path};

use lsp_types::notification::{DidChangeConfiguration, Initialized};
use lsp_types::notification::{DidChangeConfiguration, DidChangeWatchedFiles, Initialized};

use move_language_server::global_state::{initialize_new_global_state, GlobalState};
use move_language_server::main_loop::{main_loop, notification_new, request_new};
use move_language_server::main_loop::{
main_loop, notification_new, request_new, FileSystemEvent,
};
use move_language_server::server::run_server;

const SHUTDOWN_REQ_ID: u64 = 10;
Expand Down Expand Up @@ -58,20 +61,28 @@ fn response(req_id: usize, contents: serde_json::Value) -> Message {
trait MessageType {
fn into_request(self) -> Request;
fn into_response(self) -> Response;
fn into_notification(self) -> Notification;
}

impl MessageType for Message {
fn into_request(self) -> Request {
match self {
Message::Request(req) => req,
_ => panic!(),
_ => panic!("not a request"),
}
}

fn into_response(self) -> Response {
match self {
Message::Response(resp) => resp,
_ => panic!(),
_ => panic!("not a response"),
}
}

fn into_notification(self) -> Notification {
match self {
Message::Notification(notification) => notification,
_ => panic!("not a notification"),
}
}
}
Expand Down Expand Up @@ -115,13 +126,18 @@ fn test_server_initialization() {
assert_eq!(init_finished_resp.id, RequestId::from(1));
assert_eq!(
init_finished_resp.result.unwrap()["capabilities"]["textDocumentSync"],
1
serde_json::json!({"change": 1, "openClose": true})
);
let shutdown_req = client_conn.receiver.try_recv().unwrap();
let registration_req = client_conn.receiver.try_recv().unwrap().into_request();
assert_eq!(registration_req.method, "client/registerCapability");
assert_eq!(
shutdown_req.into_response().id,
RequestId::from(SHUTDOWN_REQ_ID)
registration_req.params["registrations"][0]["method"],
"workspace/didChangeWatchedFiles"
);

let shutdown_resp = client_conn.receiver.try_recv().unwrap().into_response();
assert_eq!(shutdown_resp.id, RequestId::from(SHUTDOWN_REQ_ID));

client_conn.receiver.try_recv().unwrap_err();
}

Expand Down Expand Up @@ -157,3 +173,31 @@ fn test_server_config_change() {
);
assert_eq!(global_state.config().dialect_name, DialectName::DFinance);
}

#[test]
fn test_removed_file_not_present_in_the_diagnostics() {
let (client_conn, server_conn) = Connection::memory();

let script_text = r"script {
use 0x0::Unknown;
fun main() {}
}";
let script_file = (get_script_path(), script_text.to_string());

let mut global_state = global_state(config!());
global_state.update_from_events(vec![FileSystemEvent::AddFile(script_file)]);

let delete_event = FileEvent::new(
Url::from_file_path(get_script_path()).unwrap(),
FileChangeType::Deleted,
);
let files_changed_notification =
notification::<DidChangeWatchedFiles>(DidChangeWatchedFilesParams {
changes: vec![delete_event],
});
send_messages(&client_conn, vec![files_changed_notification]);

main_loop(&mut global_state, &server_conn).unwrap();

assert!(global_state.analysis().db().available_files.is_empty());
}
8 changes: 4 additions & 4 deletions crates/move-language-server/src/global_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,17 @@ impl GlobalState {
}

pub fn initialize_new_global_state(config: Config) -> GlobalState {
let mut fs_events = vec![];
let mut initial_fs_events = vec![];
match &config.stdlib_folder {
Some(folder) => {
for file in io::load_move_files(vec![folder.clone()]).unwrap() {
fs_events.push(FileSystemEvent::AddFile(file));
initial_fs_events.push(FileSystemEvent::AddFile(file));
}
}
None => {}
}
for file in io::load_move_files(config.modules_folders.clone()).unwrap() {
fs_events.push(FileSystemEvent::AddFile(file));
initial_fs_events.push(FileSystemEvent::AddFile(file));
}
GlobalState::new(config, fs_events)
GlobalState::new(config, initial_fs_events)
}
21 changes: 19 additions & 2 deletions crates/move-language-server/src/main_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use anyhow::Result;
use crossbeam_channel::{unbounded, Sender};
use lsp_server::{Connection, Message, Notification, Request, RequestId, Response};
use lsp_types::notification::{
DidChangeConfiguration, DidChangeTextDocument, DidCloseTextDocument, DidOpenTextDocument,
PublishDiagnostics, ShowMessage,
DidChangeConfiguration, DidChangeTextDocument, DidChangeWatchedFiles, DidCloseTextDocument,
DidOpenTextDocument, PublishDiagnostics, ShowMessage,
};
use lsp_types::request::WorkspaceConfiguration;
use lsp_types::{
Expand Down Expand Up @@ -370,6 +370,23 @@ fn on_notification(
}
Err(not) => not,
};
let not = match notification_cast::<DidChangeWatchedFiles>(not) {
Ok(params) => {
for file_event in params.changes {
let uri = file_event.uri;
let fpath = uri
.to_file_path()
.map_err(|_| anyhow::anyhow!("invalid uri: {}", uri))?;
let fpath = leaked_fpath(fpath);
loop_state.opened_files.remove(fpath);
fs_events_sender
.send(FileSystemEvent::RemoveFile(fpath))
.unwrap();
}
return Ok(());
}
Err(not) => not,
};
if not.method.starts_with("$/") {
return Ok(());
}
Expand Down
42 changes: 38 additions & 4 deletions crates/move-language-server/src/server.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
use std::path::PathBuf;

use anyhow::Result;
use lsp_server::{Connection, ProtocolError};
use lsp_types::{ServerCapabilities, TextDocumentSyncCapability, TextDocumentSyncKind};
use lsp_server::{Connection, ProtocolError, RequestId};
use lsp_types::{
DidChangeWatchedFilesRegistrationOptions, FileSystemWatcher, RegistrationParams,
ServerCapabilities, TextDocumentSyncCapability, TextDocumentSyncKind,
TextDocumentSyncOptions, WatchKind,
};
use serde::de::DeserializeOwned;

use analysis::config::Config;

use crate::global_state::initialize_new_global_state;
use crate::main_loop;
use crate::main_loop::request_new;

fn move_language_server_capabilities() -> ServerCapabilities {
ServerCapabilities {
text_document_sync: Some(TextDocumentSyncCapability::Kind(TextDocumentSyncKind::Full)),
text_document_sync: Some(TextDocumentSyncCapability::Options(
TextDocumentSyncOptions {
open_close: Some(true),
change: Some(TextDocumentSyncKind::Full),
..TextDocumentSyncOptions::default()
},
)),
..ServerCapabilities::default()
}
}
Expand Down Expand Up @@ -45,13 +56,36 @@ pub fn parse_initialize_params(init_params: serde_json::Value) -> Result<(PathBu
Ok((root, config))
}

fn register_for_file_changes(connection: &Connection) {
let move_files_watcher = FileSystemWatcher {
glob_pattern: "**/*.move".to_string(),
kind: Some(WatchKind::Delete),
};
let registration_options = DidChangeWatchedFilesRegistrationOptions {
watchers: vec![move_files_watcher],
};
let registration = lsp_types::Registration {
id: "workspace/didChangeWatchedFiles".to_string(),
method: "workspace/didChangeWatchedFiles".to_string(),
register_options: Some(serde_json::to_value(registration_options).unwrap()),
};
let registration_req = request_new::<lsp_types::request::RegisterCapability>(
RequestId::from(1),
RegistrationParams {
registrations: vec![registration],
},
);
connection.sender.send(registration_req.into()).unwrap();
}

pub fn run_server(connection: &Connection) -> Result<()> {
let init_params = initialize_server(connection)?;
let (_, config) = parse_initialize_params(init_params)?;
log::info!("Initialization is finished");

register_for_file_changes(connection);

let mut global_state = initialize_new_global_state(config);
dbg!(&global_state.config());
main_loop::main_loop(&mut global_state, connection)
}

Expand Down

0 comments on commit aee35f1

Please sign in to comment.