Skip to content

Commit

Permalink
Added IDE support plus various cleanups and fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
awelc committed Feb 22, 2024
1 parent f22eb3f commit 2649072
Show file tree
Hide file tree
Showing 26 changed files with 444 additions and 209 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions external-crates/move/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions external-crates/move/crates/move-analyzer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ lsp-types.workspace = true
serde_json.workspace = true
tempfile.workspace = true
url.workspace = true
vfs.workspace = true
clap.workspace = true
crossbeam.workspace = true
move-command-line-common.workspace = true
Expand Down
49 changes: 28 additions & 21 deletions external-crates/move/crates/move-analyzer/src/bin/move-analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,11 @@ use std::{
};

use move_analyzer::{
completion::on_completion_request,
context::Context,
symbols,
vfs::{on_text_document_sync_notification, VirtualFileSystem},
completion::on_completion_request, context::Context, symbols,
vfs::on_text_document_sync_notification,
};
use url::Url;
use vfs::{impls::memory::MemoryFS, VfsPath};

#[derive(Parser)]
#[clap(author, version, about)]
Expand All @@ -48,9 +47,9 @@ fn main() {

let (connection, io_threads) = Connection::stdio();
let symbols = Arc::new(Mutex::new(symbols::empty_symbols()));
let mut context = Context {
let ide_files: VfsPath = MemoryFS::new().into();
let context = Context {
connection,
files: VirtualFileSystem::default(),
symbols: symbols.clone(),
};

Expand Down Expand Up @@ -125,7 +124,8 @@ fn main() {
.unwrap_or(false);
}

symbolicator_runner = symbols::SymbolicatorRunner::new(symbols.clone(), diag_sender, lint);
symbolicator_runner =
symbols::SymbolicatorRunner::new(ide_files.clone(), symbols.clone(), diag_sender, lint);

// If initialization information from the client contains a path to the directory being
// opened, try to initialize symbols before sending response to the client. Do not bother
Expand All @@ -134,7 +134,9 @@ fn main() {
// to be available right after the client is initialized.
if let Some(uri) = initialize_params.root_uri {
if let Some(p) = symbols::SymbolicatorRunner::root_dir(&uri.to_file_path().unwrap()) {
if let Ok((Some(new_symbols), _)) = symbols::get_symbols(p.as_path(), lint) {
if let Ok((Some(new_symbols), _)) =
symbols::get_symbols(ide_files.clone(), p.as_path(), lint)
{
let mut old_symbols = symbols.lock().unwrap();
(*old_symbols).merge(new_symbols);
}
Expand Down Expand Up @@ -189,7 +191,8 @@ fn main() {
},
}
},
Err(error) => eprintln!("symbolicator message error: {:?}", error),
Err(error) =>
eprintln!("symbolicator message error: {:?}", error),
}
},
recv(context.connection.receiver) -> message => {
Expand All @@ -199,7 +202,7 @@ fn main() {
// a chance of completing pending requests (but should not accept new requests
// either which is handled inside on_requst) - instead it quits after receiving
// the exit notification from the client, which is handled below
shutdown_req_received = on_request(&context, &request, shutdown_req_received);
shutdown_req_received = on_request(&context, &request, ide_files.clone(), shutdown_req_received);
}
Ok(Message::Response(response)) => on_response(&context, &response),
Ok(Message::Notification(notification)) => {
Expand All @@ -210,7 +213,7 @@ fn main() {
// It ought to, especially once it begins processing requests that may
// take a long time to respond to.
}
_ => on_notification(&mut context, &symbolicator_runner, &notification),
_ => on_notification(ide_files.clone(), &symbolicator_runner, &notification),
}
}
Err(error) => eprintln!("IDE message error: {:?}", error),
Expand All @@ -228,7 +231,12 @@ fn main() {
/// The reason why this information is also passed as an argument is that according to the LSP
/// spec, if any additional requests are received after shutdownd then the LSP implementation
/// should respond with a particular type of error.
fn on_request(context: &Context, request: &Request, shutdown_request_received: bool) -> bool {
fn on_request(
context: &Context,
request: &Request,
ide_files: VfsPath,
shutdown_request_received: bool,
) -> bool {
if shutdown_request_received {
let response = lsp_server::Response::new_err(
request.id.clone(),
Expand All @@ -245,9 +253,12 @@ fn on_request(context: &Context, request: &Request, shutdown_request_received: b
return true;
}
match request.method.as_str() {
lsp_types::request::Completion::METHOD => {
on_completion_request(context, request, &context.symbols.lock().unwrap())
}
lsp_types::request::Completion::METHOD => on_completion_request(
context,
request,
ide_files.clone(),
&context.symbols.lock().unwrap(),
),
lsp_types::request::GotoDefinition::METHOD => {
symbols::on_go_to_def_request(context, request, &context.symbols.lock().unwrap());
}
Expand Down Expand Up @@ -286,7 +297,7 @@ fn on_response(_context: &Context, _response: &Response) {
}

fn on_notification(
context: &mut Context,
ide_files: VfsPath,
symbolicator_runner: &symbols::SymbolicatorRunner,
notification: &Notification,
) {
Expand All @@ -295,11 +306,7 @@ fn on_notification(
| lsp_types::notification::DidChangeTextDocument::METHOD
| lsp_types::notification::DidSaveTextDocument::METHOD
| lsp_types::notification::DidCloseTextDocument::METHOD => {
on_text_document_sync_notification(
&mut context.files,
symbolicator_runner,
notification,
)
on_text_document_sync_notification(ide_files, symbolicator_runner, notification)
}
_ => eprintln!("handle notification '{}' from client", notification.method),
}
Expand Down
69 changes: 41 additions & 28 deletions external-crates/move/crates/move-analyzer/src/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use move_compiler::{
};
use move_symbol_pool::Symbol;
use std::{collections::HashSet, path::PathBuf};
use vfs::VfsPath;

/// Constructs an `lsp_types::CompletionItem` with the given `label` and `kind`.
fn completion_item(label: &str, kind: CompletionItemKind) -> CompletionItem {
Expand Down Expand Up @@ -149,7 +150,12 @@ fn get_cursor_token(buffer: &str, position: &Position) -> Option<Tok> {
/// Sends the given connection a response to a completion request.
///
/// The completions returned depend upon where the user's cursor is positioned.
pub fn on_completion_request(context: &Context, request: &Request, symbols: &Symbols) {
pub fn on_completion_request(
context: &Context,
request: &Request,
ide_files: VfsPath,
symbols: &Symbols,
) {
eprintln!("handling completion request");
let parameters = serde_json::from_value::<CompletionParams>(request.params.clone())
.expect("could not deserialize completion request");
Expand All @@ -160,38 +166,45 @@ pub fn on_completion_request(context: &Context, request: &Request, symbols: &Sym
.uri
.to_file_path()
.unwrap();
let buffer = context.files.get(&path);
if buffer.is_none() {
eprintln!(
"Could not read '{:?}' when handling completion request",
path
);
let mut buffer = String::new();
if let Some(mut f) = ide_files
.join(path.to_string_lossy().to_string())
.unwrap()
.open_file()
.ok()
{
if f.read_to_string(&mut buffer).is_err() {
eprintln!(
"Could not read '{:?}' when handling completion request",
path
);
}
}

// The completion items we provide depend upon where the user's cursor is positioned.
let cursor =
buffer.and_then(|buf| get_cursor_token(buf, &parameters.text_document_position.position));

let mut items = vec![];
match cursor {
Some(Tok::Colon) => {
items.extend_from_slice(&primitive_types());
}
Some(Tok::Period) | Some(Tok::ColonColon) => {
// `.` or `::` must be followed by identifiers, which are added to the completion items
// below.
}
_ => {
// If the user's cursor is positioned anywhere other than following a `.`, `:`, or `::`,
// offer them Move's keywords, operators, and builtins as completion items.
items.extend_from_slice(&keywords());
items.extend_from_slice(&builtins());
if !buffer.is_empty() {
let cursor = get_cursor_token(buffer.as_str(), &parameters.text_document_position.position);
match cursor {
Some(Tok::Colon) => {
items.extend_from_slice(&primitive_types());
}
Some(Tok::Period) | Some(Tok::ColonColon) => {
// `.` or `::` must be followed by identifiers, which are added to the completion items
// below.
}
_ => {
// If the user's cursor is positioned anywhere other than following a `.`, `:`, or `::`,
// offer them Move's keywords, operators, and builtins as completion items.
items.extend_from_slice(&keywords());
items.extend_from_slice(&builtins());
}
}
}

if let Some(buffer) = &buffer {
let identifiers = identifiers(buffer, symbols, &path);
let identifiers = identifiers(buffer.as_str(), symbols, &path);
items.extend_from_slice(&identifiers);
} else {
// no file content
items.extend_from_slice(&keywords());
items.extend_from_slice(&builtins());
}

let result = serde_json::to_value(items).expect("could not serialize completion response");
Expand Down
4 changes: 1 addition & 3 deletions external-crates/move/crates/move-analyzer/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@
// Copyright (c) The Move Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{symbols::Symbols, vfs::VirtualFileSystem};
use crate::symbols::Symbols;
use lsp_server::Connection;
use std::sync::{Arc, Mutex};

/// The context within which the language server is running.
pub struct Context {
/// The connection with the language server's client.
pub connection: Connection,
/// The files that the language server is providing information about.
pub files: VirtualFileSystem,
/// Symbolication information
pub symbols: Arc<Mutex<Symbols>>,
}
Loading

0 comments on commit 2649072

Please sign in to comment.