2020-02-18 05:33:16 -06:00
//! The main loop of `rust-analyzer` responsible for dispatching LSP
2020-02-18 05:25:26 -06:00
//! requests/replies and notifications back to the client.
2019-09-30 03:58:53 -05:00
2018-08-12 14:08:14 -05:00
mod handlers ;
2018-08-30 08:27:09 -05:00
mod subscriptions ;
2019-05-31 12:14:54 -05:00
pub ( crate ) mod pending_requests ;
2018-08-12 14:08:14 -05:00
2020-01-29 04:21:49 -06:00
use std ::{
2020-04-26 03:23:53 -05:00
borrow ::Cow ,
2020-01-29 04:21:49 -06:00
env ,
error ::Error ,
2020-04-26 03:23:53 -05:00
fmt ,
ops ::Range ,
panic ,
2020-01-29 04:21:49 -06:00
sync ::Arc ,
time ::{ Duration , Instant } ,
} ;
2018-08-12 16:09:30 -05:00
2020-03-30 04:46:04 -05:00
use crossbeam_channel ::{ never , select , unbounded , RecvError , Sender } ;
2019-08-30 12:18:57 -05:00
use lsp_server ::{ Connection , ErrorCode , Message , Notification , Request , RequestId , Response } ;
2020-03-13 18:03:02 -05:00
use lsp_types ::{
2020-04-26 03:23:53 -05:00
DidChangeTextDocumentParams , NumberOrString , TextDocumentContentChangeEvent , WorkDoneProgress ,
WorkDoneProgressBegin , WorkDoneProgressCreateParams , WorkDoneProgressEnd ,
WorkDoneProgressReport ,
2020-03-13 18:03:02 -05:00
} ;
2020-05-14 18:51:48 -05:00
use ra_flycheck ::{ CheckTask , Status } ;
2020-04-26 03:23:53 -05:00
use ra_ide ::{ Canceled , FileId , LibraryData , LineIndex , SourceRootId } ;
2019-07-04 15:05:17 -05:00
use ra_prof ::profile ;
2020-04-01 05:40:40 -05:00
use ra_project_model ::{ PackageRoot , ProjectWorkspace } ;
2020-06-13 04:00:06 -05:00
use ra_vfs ::{ VfsTask , Watch } ;
2019-08-31 06:47:37 -05:00
use relative_path ::RelativePathBuf ;
2019-09-06 08:25:24 -05:00
use rustc_hash ::FxHashSet ;
2018-10-15 16:44:23 -05:00
use serde ::{ de ::DeserializeOwned , Serialize } ;
2019-01-06 02:41:11 -06:00
use threadpool ::ThreadPool ;
2018-08-12 16:09:30 -05:00
2018-10-15 12:15:53 -05:00
use crate ::{
2020-06-03 05:22:01 -05:00
config ::{ Config , FilesWatcher , LinkedProject } ,
2020-06-13 04:00:06 -05:00
diagnostics ::DiagnosticTask ,
2020-06-03 04:16:08 -05:00
from_proto ,
2020-06-13 04:00:06 -05:00
global_state ::{ file_id_to_url , GlobalState , GlobalStateSnapshot } ,
2020-06-03 04:16:08 -05:00
lsp_ext ,
2019-05-31 12:14:54 -05:00
main_loop ::{
2019-07-04 15:05:17 -05:00
pending_requests ::{ PendingRequest , PendingRequests } ,
2019-05-31 12:14:54 -05:00
subscriptions ::Subscriptions ,
} ,
2020-04-01 11:41:43 -05:00
Result ,
2018-08-12 14:08:14 -05:00
} ;
2019-06-14 15:42:56 -05:00
#[ derive(Debug) ]
2018-10-22 12:49:27 -05:00
pub struct LspError {
pub code : i32 ,
pub message : String ,
}
impl LspError {
2020-03-09 04:54:14 -05:00
pub const UNKNOWN_FILE : i32 = - 32900 ;
2018-10-22 12:49:27 -05:00
pub fn new ( code : i32 , message : String ) -> LspError {
2018-10-31 15:41:43 -05:00
LspError { code , message }
2018-10-22 12:49:27 -05:00
}
}
2019-06-14 15:42:56 -05:00
impl fmt ::Display for LspError {
fn fmt ( & self , f : & mut fmt ::Formatter ) -> fmt ::Result {
write! ( f , " Language Server request failed with {}. ({}) " , self . code , self . message )
}
}
impl Error for LspError { }
2020-06-03 05:22:01 -05:00
pub fn main_loop ( config : Config , connection : Connection ) -> Result < ( ) > {
2020-04-01 10:22:56 -05:00
log ::info! ( " initial config: {:#?} " , config ) ;
2019-08-31 06:47:37 -05:00
2020-01-26 05:02:56 -06:00
// Windows scheduler implements priority boosts: if thread waits for an
// event (like a condvar), and event fires, priority of the thread is
// temporary bumped. This optimization backfires in our case: each time the
// `main_loop` schedules a task to run on a threadpool, the worker threads
// gets a higher priority, and (on a machine with fewer cores) displaces the
// main loop! We work-around this by marking the main loop as a
// higher-priority thread.
//
// https://docs.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities
// https://docs.microsoft.com/en-us/windows/win32/procthread/priority-boosts
// https://github.com/rust-analyzer/rust-analyzer/issues/2835
#[ cfg(windows) ]
unsafe {
use winapi ::um ::processthreadsapi ::* ;
let thread = GetCurrentThread ( ) ;
let thread_priority_above_normal = 1 ;
SetThreadPriority ( thread , thread_priority_above_normal ) ;
}
2019-09-06 08:25:24 -05:00
let mut loop_state = LoopState ::default ( ) ;
2020-06-03 04:16:08 -05:00
let mut global_state = {
2019-09-06 08:25:24 -05:00
let workspaces = {
2020-06-03 05:22:01 -05:00
if config . linked_projects . is_empty ( ) & & config . notifications . cargo_toml_not_found {
2020-04-16 15:19:38 -05:00
show_message (
2020-05-10 12:24:02 -05:00
lsp_types ::MessageType ::Error ,
2020-06-03 05:22:01 -05:00
" rust-analyzer failed to discover workspace " . to_string ( ) ,
2020-05-08 18:24:51 -05:00
& connection . sender ,
) ;
2020-04-16 15:19:38 -05:00
} ;
2020-04-16 15:02:10 -05:00
2020-06-03 05:22:01 -05:00
config
. linked_projects
. iter ( )
. filter_map ( | project | match project {
2020-06-03 07:48:38 -05:00
LinkedProject ::ProjectManifest ( manifest ) = > {
ra_project_model ::ProjectWorkspace ::load (
manifest . clone ( ) ,
& config . cargo ,
config . with_sysroot ,
)
. map_err ( | err | {
log ::error! ( " failed to load workspace: {:#} " , err ) ;
show_message (
lsp_types ::MessageType ::Error ,
format! ( " rust-analyzer failed to load workspace: {:#} " , err ) ,
& connection . sender ,
) ;
} )
. ok ( )
}
2020-06-09 14:26:42 -05:00
LinkedProject ::InlineJsonProject ( it ) = > {
Some ( ra_project_model ::ProjectWorkspace ::Json {
project : it . clone ( ) ,
project_location : config . root_path . clone ( ) ,
} )
}
2020-04-16 15:02:10 -05:00
} )
. collect ::< Vec < _ > > ( )
2019-09-06 08:25:24 -05:00
} ;
2019-03-05 13:59:01 -06:00
2019-09-06 08:25:24 -05:00
let globs = config
2020-04-02 05:47:58 -05:00
. files
. exclude
2019-09-06 08:25:24 -05:00
. iter ( )
2020-02-17 12:07:30 -06:00
. map ( | glob | crate ::vfs_glob ::Glob ::new ( glob ) )
2019-09-06 08:25:24 -05:00
. collect ::< std ::result ::Result < Vec < _ > , _ > > ( ) ? ;
2020-04-02 05:47:58 -05:00
if let FilesWatcher ::Client = config . files . watcher {
2020-05-10 12:24:02 -05:00
let registration_options = lsp_types ::DidChangeWatchedFilesRegistrationOptions {
2019-09-06 08:25:24 -05:00
watchers : workspaces
. iter ( )
2020-04-01 05:40:40 -05:00
. flat_map ( ProjectWorkspace ::to_roots )
. filter ( PackageRoot ::is_member )
. map ( | root | format! ( " {} /**/*.rs " , root . path ( ) . display ( ) ) )
2020-05-10 12:24:02 -05:00
. map ( | glob_pattern | lsp_types ::FileSystemWatcher { glob_pattern , kind : None } )
2019-09-06 08:25:24 -05:00
. collect ( ) ,
} ;
2020-05-10 12:24:02 -05:00
let registration = lsp_types ::Registration {
2019-09-06 08:25:24 -05:00
id : " file-watcher " . to_string ( ) ,
method : " workspace/didChangeWatchedFiles " . to_string ( ) ,
register_options : Some ( serde_json ::to_value ( registration_options ) . unwrap ( ) ) ,
} ;
2020-05-10 12:24:02 -05:00
let params = lsp_types ::RegistrationParams { registrations : vec ! [ registration ] } ;
let request = request_new ::< lsp_types ::request ::RegisterCapability > (
loop_state . next_request_id ( ) ,
params ,
) ;
2019-09-06 08:25:24 -05:00
connection . sender . send ( request . into ( ) ) . unwrap ( ) ;
}
2020-06-03 04:16:08 -05:00
GlobalState ::new (
2019-09-06 08:25:24 -05:00
workspaces ,
config . lru_capacity ,
& globs ,
2020-04-02 05:47:58 -05:00
Watch ( matches! ( config . files . watcher , FilesWatcher ::Notify ) ) ,
2020-04-01 10:22:56 -05:00
config ,
2019-09-06 08:25:24 -05:00
)
2019-08-22 06:44:16 -05:00
} ;
2018-12-19 06:04:15 -06:00
2020-06-03 04:16:08 -05:00
loop_state . roots_total = global_state . vfs . read ( ) . n_roots ( ) ;
2020-03-28 17:33:16 -05:00
2020-01-25 06:27:36 -06:00
let pool = ThreadPool ::default ( ) ;
2019-05-31 12:14:54 -05:00
let ( task_sender , task_receiver ) = unbounded ::< Task > ( ) ;
2019-08-31 06:47:37 -05:00
let ( libdata_sender , libdata_receiver ) = unbounded ::< LibraryData > ( ) ;
2018-08-17 11:54:08 -05:00
2019-05-31 12:14:54 -05:00
log ::info! ( " server initialized, serving requests " ) ;
2019-08-31 06:47:37 -05:00
{
let task_sender = task_sender ;
let libdata_sender = libdata_sender ;
loop {
log ::trace! ( " selecting " ) ;
let event = select! {
recv ( & connection . receiver ) -> msg = > match msg {
Ok ( msg ) = > Event ::Msg ( msg ) ,
2020-02-18 06:53:02 -06:00
Err ( RecvError ) = > return Err ( " client exited without shutdown " . into ( ) ) ,
2019-08-31 06:47:37 -05:00
} ,
recv ( task_receiver ) -> task = > Event ::Task ( task . unwrap ( ) ) ,
2020-06-03 04:16:08 -05:00
recv ( global_state . task_receiver ) -> task = > match task {
2019-08-31 06:47:37 -05:00
Ok ( task ) = > Event ::Vfs ( task ) ,
2020-02-18 06:53:02 -06:00
Err ( RecvError ) = > return Err ( " vfs died " . into ( ) ) ,
2019-08-31 06:47:37 -05:00
} ,
2019-12-25 05:21:38 -06:00
recv ( libdata_receiver ) -> data = > Event ::Lib ( data . unwrap ( ) ) ,
2020-06-03 04:16:08 -05:00
recv ( global_state . flycheck . as_ref ( ) . map_or ( & never ( ) , | it | & it . task_recv ) ) -> task = > match task {
2019-12-27 05:43:14 -06:00
Ok ( task ) = > Event ::CheckWatcher ( task ) ,
2020-02-18 06:53:02 -06:00
Err ( RecvError ) = > return Err ( " check watcher died " . into ( ) ) ,
2019-12-27 05:43:14 -06:00
}
2019-08-31 06:47:37 -05:00
} ;
if let Event ::Msg ( Message ::Request ( req ) ) = & event {
if connection . handle_shutdown ( & req ) ? {
break ;
} ;
}
2020-03-20 15:09:23 -05:00
loop_turn (
2019-08-31 06:47:37 -05:00
& pool ,
& task_sender ,
& libdata_sender ,
2019-09-06 12:58:21 -05:00
& connection ,
2020-06-03 04:16:08 -05:00
& mut global_state ,
2019-08-31 06:47:37 -05:00
& mut loop_state ,
event ,
2020-03-20 15:09:23 -05:00
) ? ;
2019-08-31 06:47:37 -05:00
}
}
2020-06-03 04:16:08 -05:00
global_state . analysis_host . request_cancellation ( ) ;
2018-12-06 12:03:39 -06:00
log ::info! ( " waiting for tasks to finish... " ) ;
2019-08-31 06:47:37 -05:00
task_receiver . into_iter ( ) . for_each ( | task | {
2020-06-03 04:16:08 -05:00
on_task ( task , & connection . sender , & mut loop_state . pending_requests , & mut global_state )
2019-08-31 06:47:37 -05:00
} ) ;
2019-10-30 12:36:37 -05:00
libdata_receiver . into_iter ( ) . for_each ( drop ) ;
2018-12-06 12:03:39 -06:00
log ::info! ( " ...tasks have finished " ) ;
log ::info! ( " joining threadpool... " ) ;
2020-04-25 09:26:33 -05:00
pool . join ( ) ;
2018-09-04 12:43:37 -05:00
drop ( pool ) ;
2018-12-06 12:03:39 -06:00
log ::info! ( " ...threadpool has finished " ) ;
2018-09-01 09:40:45 -05:00
2020-06-03 04:16:08 -05:00
let vfs = Arc ::try_unwrap ( global_state . vfs ) . expect ( " all snapshots should be dead " ) ;
2019-02-14 11:43:45 -06:00
drop ( vfs ) ;
2018-09-02 06:46:15 -05:00
2019-08-31 06:47:37 -05:00
Ok ( ( ) )
2018-09-01 09:40:45 -05:00
}
2019-05-31 12:14:54 -05:00
#[ derive(Debug) ]
enum Task {
2019-08-30 09:24:11 -05:00
Respond ( Response ) ,
Notify ( Notification ) ,
2020-01-31 12:23:25 -06:00
Diagnostic ( DiagnosticTask ) ,
2019-05-31 12:14:54 -05:00
}
2018-12-22 03:13:20 -06:00
enum Event {
2019-08-30 09:24:11 -05:00
Msg ( Message ) ,
2018-12-22 03:13:20 -06:00
Task ( Task ) ,
Vfs ( VfsTask ) ,
Lib ( LibraryData ) ,
2019-12-25 05:21:38 -06:00
CheckWatcher ( CheckTask ) ,
2018-12-22 03:13:20 -06:00
}
impl fmt ::Debug for Event {
fn fmt ( & self , f : & mut fmt ::Formatter ) -> fmt ::Result {
2019-08-30 09:24:11 -05:00
let debug_verbose_not = | not : & Notification , f : & mut fmt ::Formatter | {
f . debug_struct ( " Notification " ) . field ( " method " , & not . method ) . finish ( )
2018-12-22 03:13:20 -06:00
} ;
match self {
2019-08-30 09:24:11 -05:00
Event ::Msg ( Message ::Notification ( not ) ) = > {
2020-05-10 12:24:02 -05:00
if notification_is ::< lsp_types ::notification ::DidOpenTextDocument > ( not )
| | notification_is ::< lsp_types ::notification ::DidChangeTextDocument > ( not )
2019-08-30 09:24:11 -05:00
{
2018-12-22 03:13:20 -06:00
return debug_verbose_not ( not , f ) ;
}
}
Event ::Task ( Task ::Notify ( not ) ) = > {
2020-05-10 12:24:02 -05:00
if notification_is ::< lsp_types ::notification ::PublishDiagnostics > ( not ) {
2018-12-22 03:13:20 -06:00
return debug_verbose_not ( not , f ) ;
}
}
2018-12-22 06:09:08 -06:00
Event ::Task ( Task ::Respond ( resp ) ) = > {
return f
2019-08-30 09:24:11 -05:00
. debug_struct ( " Response " )
2018-12-22 06:09:08 -06:00
. field ( " id " , & resp . id )
. field ( " error " , & resp . error )
. finish ( ) ;
}
2018-12-22 03:13:20 -06:00
_ = > ( ) ,
}
match self {
Event ::Msg ( it ) = > fmt ::Debug ::fmt ( it , f ) ,
Event ::Task ( it ) = > fmt ::Debug ::fmt ( it , f ) ,
Event ::Vfs ( it ) = > fmt ::Debug ::fmt ( it , f ) ,
Event ::Lib ( it ) = > fmt ::Debug ::fmt ( it , f ) ,
2019-12-25 05:21:38 -06:00
Event ::CheckWatcher ( it ) = > fmt ::Debug ::fmt ( it , f ) ,
2018-12-22 03:13:20 -06:00
}
}
}
2019-08-31 06:47:37 -05:00
#[ derive(Debug, Default) ]
struct LoopState {
2019-09-06 08:25:24 -05:00
next_request_id : u64 ,
pending_responses : FxHashSet < RequestId > ,
2019-08-31 06:47:37 -05:00
pending_requests : PendingRequests ,
subscriptions : Subscriptions ,
// We try not to index more than MAX_IN_FLIGHT_LIBS libraries at the same
// time to always have a thread ready to react to input.
in_flight_libraries : usize ,
pending_libraries : Vec < ( SourceRootId , Vec < ( FileId , RelativePathBuf , Arc < String > ) > ) > ,
workspace_loaded : bool ,
2020-03-29 13:39:03 -05:00
roots_progress_reported : Option < usize > ,
roots_scanned : usize ,
2020-03-28 17:33:16 -05:00
roots_total : usize ,
2020-03-19 16:56:32 -05:00
configuration_request_id : Option < RequestId > ,
2019-08-31 06:47:37 -05:00
}
2019-09-06 08:25:24 -05:00
impl LoopState {
fn next_request_id ( & mut self ) -> RequestId {
self . next_request_id + = 1 ;
let res : RequestId = self . next_request_id . into ( ) ;
let inserted = self . pending_responses . insert ( res . clone ( ) ) ;
assert! ( inserted ) ;
res
}
}
2019-08-31 06:47:37 -05:00
fn loop_turn (
2018-09-01 09:40:45 -05:00
pool : & ThreadPool ,
2019-08-31 06:47:37 -05:00
task_sender : & Sender < Task > ,
libdata_sender : & Sender < LibraryData > ,
2019-08-30 12:18:57 -05:00
connection : & Connection ,
2020-06-03 04:16:08 -05:00
global_state : & mut GlobalState ,
2019-08-31 06:47:37 -05:00
loop_state : & mut LoopState ,
event : Event ,
2020-03-20 15:09:23 -05:00
) -> Result < ( ) > {
2019-08-31 06:47:37 -05:00
let loop_start = Instant ::now ( ) ;
2019-01-11 07:58:01 -06:00
2019-08-31 06:47:37 -05:00
// NOTE: don't count blocking select! call as a loop-turn time
let _p = profile ( " main_loop_inner/loop-turn " ) ;
log ::info! ( " loop turn = {:?} " , event ) ;
let queue_count = pool . queued_count ( ) ;
if queue_count > 0 {
log ::info! ( " queued count = {} " , queue_count ) ;
}
2019-05-29 06:59:01 -05:00
2019-08-31 06:47:37 -05:00
match event {
Event ::Task ( task ) = > {
2020-06-03 04:16:08 -05:00
on_task ( task , & connection . sender , & mut loop_state . pending_requests , global_state ) ;
global_state . maybe_collect_garbage ( ) ;
2019-05-29 06:34:21 -05:00
}
2019-08-31 06:47:37 -05:00
Event ::Vfs ( task ) = > {
2020-06-03 04:16:08 -05:00
global_state . vfs . write ( ) . handle_task ( task ) ;
2019-08-31 06:47:37 -05:00
}
Event ::Lib ( lib ) = > {
2020-06-03 04:16:08 -05:00
global_state . add_lib ( lib ) ;
global_state . maybe_collect_garbage ( ) ;
2019-08-31 06:47:37 -05:00
loop_state . in_flight_libraries - = 1 ;
2020-03-29 13:39:03 -05:00
loop_state . roots_scanned + = 1 ;
2019-08-31 06:47:37 -05:00
}
2020-06-03 04:16:08 -05:00
Event ::CheckWatcher ( task ) = > on_check_task ( task , global_state , task_sender ) ? ,
2019-08-31 06:47:37 -05:00
Event ::Msg ( msg ) = > match msg {
Message ::Request ( req ) = > on_request (
2020-06-03 04:16:08 -05:00
global_state ,
2019-08-31 06:47:37 -05:00
& mut loop_state . pending_requests ,
pool ,
task_sender ,
& connection . sender ,
loop_start ,
req ,
) ? ,
Message ::Notification ( not ) = > {
2020-06-03 04:16:08 -05:00
on_notification ( & connection . sender , global_state , loop_state , not ) ? ;
2018-08-13 05:46:05 -05:00
}
2019-09-06 08:25:24 -05:00
Message ::Response ( resp ) = > {
let removed = loop_state . pending_responses . remove ( & resp . id ) ;
if ! removed {
log ::error! ( " unexpected response: {:?} " , resp )
}
2020-03-20 17:01:47 -05:00
2020-03-24 14:43:22 -05:00
if Some ( & resp . id ) = = loop_state . configuration_request_id . as_ref ( ) {
2020-03-21 16:58:52 -05:00
loop_state . configuration_request_id = None ;
2020-03-24 14:43:22 -05:00
log ::debug! ( " config update response: '{:?} " , resp ) ;
2020-03-24 14:06:03 -05:00
let Response { error , result , .. } = resp ;
2020-03-24 14:43:22 -05:00
2020-04-01 11:41:43 -05:00
match ( error , result ) {
2020-03-24 14:06:03 -05:00
( Some ( err ) , _ ) = > {
log ::error! ( " failed to fetch the server settings: {:?} " , err )
}
2020-04-01 12:27:45 -05:00
( None , Some ( configs ) ) = > {
if let Some ( new_config ) = configs . get ( 0 ) {
2020-06-03 04:16:08 -05:00
let mut config = global_state . config . clone ( ) ;
2020-04-01 12:27:45 -05:00
config . update ( & new_config ) ;
2020-06-03 04:16:08 -05:00
global_state . update_configuration ( config ) ;
2020-04-01 12:27:45 -05:00
}
2020-03-24 14:43:22 -05:00
}
2020-03-24 14:06:03 -05:00
( None , None ) = > {
log ::error! ( " received empty server settings response from the client " )
}
2020-03-20 17:01:47 -05:00
}
2020-03-19 17:49:26 -05:00
}
2019-09-06 08:25:24 -05:00
}
2019-08-31 06:47:37 -05:00
} ,
} ;
2018-08-30 08:27:09 -05:00
2019-12-22 06:56:19 -06:00
let mut state_changed = false ;
2020-06-03 04:16:08 -05:00
if let Some ( changes ) = global_state . process_changes ( & mut loop_state . roots_scanned ) {
2019-12-22 06:56:19 -06:00
state_changed = true ;
loop_state . pending_libraries . extend ( changes ) ;
}
2020-01-25 06:27:36 -06:00
let max_in_flight_libs = pool . max_count ( ) . saturating_sub ( 2 ) . max ( 1 ) ;
2020-05-09 12:52:52 -05:00
while loop_state . in_flight_libraries < max_in_flight_libs {
let ( root , files ) = match loop_state . pending_libraries . pop ( ) {
Some ( it ) = > it ,
None = > break ,
} ;
2019-08-31 06:47:37 -05:00
loop_state . in_flight_libraries + = 1 ;
let sender = libdata_sender . clone ( ) ;
pool . execute ( move | | {
log ::info! ( " indexing {:?} ... " , root ) ;
let data = LibraryData ::prepare ( root , files ) ;
sender . send ( data ) . unwrap ( ) ;
} ) ;
}
2019-01-11 07:58:01 -06:00
2020-05-09 16:15:22 -05:00
let show_progress =
2020-06-03 04:16:08 -05:00
! loop_state . workspace_loaded & & global_state . config . client_caps . work_done_progress ;
2020-03-28 17:33:16 -05:00
2019-08-31 06:47:37 -05:00
if ! loop_state . workspace_loaded
2020-03-29 13:39:03 -05:00
& & loop_state . roots_scanned = = loop_state . roots_total
2019-08-31 06:47:37 -05:00
& & loop_state . pending_libraries . is_empty ( )
& & loop_state . in_flight_libraries = = 0
{
2020-04-24 22:30:49 -05:00
state_changed = true ;
2019-08-31 06:47:37 -05:00
loop_state . workspace_loaded = true ;
2020-06-03 04:16:08 -05:00
if let Some ( flycheck ) = & global_state . flycheck {
2020-04-01 04:09:19 -05:00
flycheck . update ( ) ;
2020-03-30 04:46:04 -05:00
}
2020-03-28 17:33:16 -05:00
}
if show_progress {
send_startup_progress ( & connection . sender , loop_state ) ;
2019-08-31 06:47:37 -05:00
}
2018-12-19 06:04:15 -06:00
2020-04-24 22:30:49 -05:00
if state_changed & & loop_state . workspace_loaded {
2019-08-31 06:47:37 -05:00
update_file_notifications_on_threadpool (
pool ,
2020-06-03 04:16:08 -05:00
global_state . snapshot ( ) ,
2019-08-31 06:47:37 -05:00
task_sender . clone ( ) ,
loop_state . subscriptions . subscriptions ( ) ,
2020-04-24 22:30:49 -05:00
) ;
pool . execute ( {
let subs = loop_state . subscriptions . subscriptions ( ) ;
2020-06-03 04:16:08 -05:00
let snap = global_state . snapshot ( ) ;
2020-04-24 22:30:49 -05:00
move | | snap . analysis ( ) . prime_caches ( subs ) . unwrap_or_else ( | _ : Canceled | ( ) )
} ) ;
2018-08-12 14:08:14 -05:00
}
2020-01-29 04:21:49 -06:00
let loop_duration = loop_start . elapsed ( ) ;
2020-01-29 07:04:10 -06:00
if loop_duration > Duration ::from_millis ( 100 ) {
2020-01-29 04:21:49 -06:00
log ::error! ( " overly long loop turn: {:?} " , loop_duration ) ;
if env ::var ( " RA_PROFILE " ) . is_ok ( ) {
show_message (
2020-05-10 12:24:02 -05:00
lsp_types ::MessageType ::Error ,
2020-01-29 04:21:49 -06:00
format! ( " overly long loop turn: {:?} " , loop_duration ) ,
& connection . sender ,
) ;
}
}
2020-03-20 15:09:23 -05:00
Ok ( ( ) )
2018-08-12 14:08:14 -05:00
}
2019-05-29 06:59:01 -05:00
fn on_task (
task : Task ,
2019-08-30 09:24:11 -05:00
msg_sender : & Sender < Message > ,
2019-05-31 12:14:54 -05:00
pending_requests : & mut PendingRequests ,
2020-06-03 04:16:08 -05:00
state : & mut GlobalState ,
2019-05-29 06:59:01 -05:00
) {
2018-09-01 10:03:57 -05:00
match task {
Task ::Respond ( response ) = > {
2019-08-30 09:24:11 -05:00
if let Some ( completed ) = pending_requests . finish ( & response . id ) {
2019-05-29 07:42:14 -05:00
log ::info! ( " handled req#{} in {:?} " , completed . id , completed . duration ) ;
state . complete_request ( completed ) ;
2019-03-05 07:24:59 -06:00
msg_sender . send ( response . into ( ) ) . unwrap ( ) ;
2018-09-01 10:03:57 -05:00
}
}
2018-12-30 14:23:31 -06:00
Task ::Notify ( n ) = > {
2019-03-05 07:24:59 -06:00
msg_sender . send ( n . into ( ) ) . unwrap ( ) ;
2018-12-30 14:23:31 -06:00
}
2020-01-31 12:23:25 -06:00
Task ::Diagnostic ( task ) = > on_diagnostic_task ( task , msg_sender , state ) ,
2018-09-01 10:03:57 -05:00
}
}
2018-08-12 16:09:30 -05:00
fn on_request (
2020-06-03 04:16:08 -05:00
global_state : & mut GlobalState ,
2019-05-31 12:14:54 -05:00
pending_requests : & mut PendingRequests ,
2018-08-12 16:09:30 -05:00
pool : & ThreadPool ,
2020-01-29 04:15:08 -06:00
task_sender : & Sender < Task > ,
2019-08-30 09:24:11 -05:00
msg_sender : & Sender < Message > ,
2019-05-29 06:59:01 -05:00
request_received : Instant ,
2019-08-30 09:24:11 -05:00
req : Request ,
2019-05-31 12:42:53 -05:00
) -> Result < ( ) > {
let mut pool_dispatcher = PoolDispatcher {
req : Some ( req ) ,
pool ,
2020-06-03 04:16:08 -05:00
global_state ,
2020-01-29 04:15:08 -06:00
task_sender ,
2019-05-31 12:42:53 -05:00
msg_sender ,
pending_requests ,
request_received ,
} ;
pool_dispatcher
2020-05-10 12:25:37 -05:00
. on_sync ::< lsp_ext ::CollectGarbage > ( | s , ( ) | Ok ( s . collect_garbage ( ) ) ) ?
. on_sync ::< lsp_ext ::JoinLines > ( | s , p | handlers ::handle_join_lines ( s . snapshot ( ) , p ) ) ?
. on_sync ::< lsp_ext ::OnEnter > ( | s , p | handlers ::handle_on_enter ( s . snapshot ( ) , p ) ) ?
2020-05-10 12:24:02 -05:00
. on_sync ::< lsp_types ::request ::SelectionRangeRequest > ( | s , p | {
2019-05-31 12:52:09 -05:00
handlers ::handle_selection_range ( s . snapshot ( ) , p )
} ) ?
2020-05-24 10:04:17 -05:00
. on_sync ::< lsp_ext ::MatchingBrace > ( | s , p | handlers ::handle_matching_brace ( s . snapshot ( ) , p ) ) ?
2020-05-10 12:25:37 -05:00
. on ::< lsp_ext ::AnalyzerStatus > ( handlers ::handle_analyzer_status ) ?
. on ::< lsp_ext ::SyntaxTree > ( handlers ::handle_syntax_tree ) ?
. on ::< lsp_ext ::ExpandMacro > ( handlers ::handle_expand_macro ) ?
. on ::< lsp_ext ::ParentModule > ( handlers ::handle_parent_module ) ?
. on ::< lsp_ext ::Runnables > ( handlers ::handle_runnables ) ?
. on ::< lsp_ext ::InlayHints > ( handlers ::handle_inlay_hints ) ?
2020-05-17 17:11:40 -05:00
. on ::< lsp_ext ::CodeActionRequest > ( handlers ::handle_code_action ) ?
2020-06-02 15:21:48 -05:00
. on ::< lsp_ext ::ResolveCodeActionRequest > ( handlers ::handle_resolve_code_action ) ?
2020-06-03 06:15:54 -05:00
. on ::< lsp_ext ::HoverRequest > ( handlers ::handle_hover ) ?
2020-05-10 12:24:02 -05:00
. on ::< lsp_types ::request ::OnTypeFormatting > ( handlers ::handle_on_type_formatting ) ?
. on ::< lsp_types ::request ::DocumentSymbolRequest > ( handlers ::handle_document_symbol ) ?
. on ::< lsp_types ::request ::WorkspaceSymbol > ( handlers ::handle_workspace_symbol ) ?
. on ::< lsp_types ::request ::GotoDefinition > ( handlers ::handle_goto_definition ) ?
. on ::< lsp_types ::request ::GotoImplementation > ( handlers ::handle_goto_implementation ) ?
. on ::< lsp_types ::request ::GotoTypeDefinition > ( handlers ::handle_goto_type_definition ) ?
. on ::< lsp_types ::request ::Completion > ( handlers ::handle_completion ) ?
. on ::< lsp_types ::request ::CodeLensRequest > ( handlers ::handle_code_lens ) ?
. on ::< lsp_types ::request ::CodeLensResolve > ( handlers ::handle_code_lens_resolve ) ?
. on ::< lsp_types ::request ::FoldingRangeRequest > ( handlers ::handle_folding_range ) ?
. on ::< lsp_types ::request ::SignatureHelpRequest > ( handlers ::handle_signature_help ) ?
. on ::< lsp_types ::request ::PrepareRenameRequest > ( handlers ::handle_prepare_rename ) ?
. on ::< lsp_types ::request ::Rename > ( handlers ::handle_rename ) ?
. on ::< lsp_types ::request ::References > ( handlers ::handle_references ) ?
. on ::< lsp_types ::request ::Formatting > ( handlers ::handle_formatting ) ?
. on ::< lsp_types ::request ::DocumentHighlightRequest > ( handlers ::handle_document_highlight ) ?
. on ::< lsp_types ::request ::CallHierarchyPrepare > ( handlers ::handle_call_hierarchy_prepare ) ?
. on ::< lsp_types ::request ::CallHierarchyIncomingCalls > (
handlers ::handle_call_hierarchy_incoming ,
) ?
. on ::< lsp_types ::request ::CallHierarchyOutgoingCalls > (
handlers ::handle_call_hierarchy_outgoing ,
) ?
. on ::< lsp_types ::request ::SemanticTokensRequest > ( handlers ::handle_semantic_tokens ) ?
. on ::< lsp_types ::request ::SemanticTokensRangeRequest > (
handlers ::handle_semantic_tokens_range ,
) ?
2020-05-10 12:25:37 -05:00
. on ::< lsp_ext ::Ssr > ( handlers ::handle_ssr ) ?
2018-08-31 04:04:33 -05:00
. finish ( ) ;
2019-05-31 12:42:53 -05:00
Ok ( ( ) )
2018-08-12 14:08:14 -05:00
}
2018-08-12 16:09:30 -05:00
fn on_notification (
2019-08-30 09:24:11 -05:00
msg_sender : & Sender < Message > ,
2020-06-03 04:16:08 -05:00
state : & mut GlobalState ,
2020-03-19 16:56:32 -05:00
loop_state : & mut LoopState ,
2019-08-30 09:24:11 -05:00
not : Notification ,
2018-08-12 16:09:30 -05:00
) -> Result < ( ) > {
2020-05-10 12:24:02 -05:00
let not = match notification_cast ::< lsp_types ::notification ::Cancel > ( not ) {
2018-09-01 09:40:45 -05:00
Ok ( params ) = > {
2019-08-30 09:24:11 -05:00
let id : RequestId = match params . id {
NumberOrString ::Number ( id ) = > id . into ( ) ,
NumberOrString ::String ( id ) = > id . into ( ) ,
2018-09-01 09:40:45 -05:00
} ;
2020-03-19 16:56:32 -05:00
if loop_state . pending_requests . cancel ( & id ) {
2019-08-30 09:24:11 -05:00
let response = Response ::new_err (
2018-12-09 05:43:02 -06:00
id ,
2019-01-08 17:47:12 -06:00
ErrorCode ::RequestCanceled as i32 ,
2018-12-09 05:43:02 -06:00
" canceled by client " . to_string ( ) ,
) ;
2019-03-05 07:24:59 -06:00
msg_sender . send ( response . into ( ) ) . unwrap ( )
2018-12-09 05:43:02 -06:00
}
2018-10-15 16:44:23 -05:00
return Ok ( ( ) ) ;
2018-08-31 04:04:33 -05:00
}
2018-09-01 09:40:45 -05:00
Err ( not ) = > not ,
} ;
2020-05-10 12:24:02 -05:00
let not = match notification_cast ::< lsp_types ::notification ::DidOpenTextDocument > ( not ) {
2018-09-01 09:40:45 -05:00
Ok ( params ) = > {
let uri = params . text_document . uri ;
2019-06-14 15:42:56 -05:00
let path = uri . to_file_path ( ) . map_err ( | ( ) | format! ( " invalid uri: {} " , uri ) ) ? ;
2019-02-08 05:49:43 -06:00
if let Some ( file_id ) =
state . vfs . write ( ) . add_file_overlay ( & path , params . text_document . text )
2018-12-19 06:04:15 -06:00
{
2020-03-19 16:56:32 -05:00
loop_state . subscriptions . add_sub ( FileId ( file_id . 0 ) ) ;
2018-12-19 06:04:15 -06:00
}
2018-10-15 16:44:23 -05:00
return Ok ( ( ) ) ;
2018-09-01 09:40:45 -05:00
}
Err ( not ) = > not ,
} ;
2020-05-10 12:24:02 -05:00
let not = match notification_cast ::< lsp_types ::notification ::DidChangeTextDocument > ( not ) {
2020-04-26 03:23:53 -05:00
Ok ( params ) = > {
let DidChangeTextDocumentParams { text_document , content_changes } = params ;
let world = state . snapshot ( ) ;
2020-05-10 06:55:24 -05:00
let file_id = from_proto ::file_id ( & world , & text_document . uri ) ? ;
2020-04-26 03:23:53 -05:00
let line_index = world . analysis ( ) . file_line_index ( file_id ) ? ;
let uri = text_document . uri ;
2019-06-14 15:42:56 -05:00
let path = uri . to_file_path ( ) . map_err ( | ( ) | format! ( " invalid uri: {} " , uri ) ) ? ;
2020-04-26 03:23:53 -05:00
state . vfs . write ( ) . change_file_overlay ( & path , | old_text | {
apply_document_changes ( old_text , Cow ::Borrowed ( & line_index ) , content_changes ) ;
} ) ;
2018-10-15 16:44:23 -05:00
return Ok ( ( ) ) ;
2018-09-01 09:40:45 -05:00
}
Err ( not ) = > not ,
} ;
2020-05-10 12:24:02 -05:00
let not = match notification_cast ::< lsp_types ::notification ::DidSaveTextDocument > ( not ) {
2019-12-25 05:21:38 -06:00
Ok ( _params ) = > {
2020-04-01 04:09:19 -05:00
if let Some ( flycheck ) = & state . flycheck {
flycheck . update ( ) ;
2020-03-30 04:46:04 -05:00
}
2019-12-25 05:21:38 -06:00
return Ok ( ( ) ) ;
}
Err ( not ) = > not ,
} ;
2020-05-10 12:24:02 -05:00
let not = match notification_cast ::< lsp_types ::notification ::DidCloseTextDocument > ( not ) {
2018-09-01 09:40:45 -05:00
Ok ( params ) = > {
let uri = params . text_document . uri ;
2019-06-14 15:42:56 -05:00
let path = uri . to_file_path ( ) . map_err ( | ( ) | format! ( " invalid uri: {} " , uri ) ) ? ;
2018-12-19 06:04:15 -06:00
if let Some ( file_id ) = state . vfs . write ( ) . remove_file_overlay ( path . as_path ( ) ) {
2020-03-19 16:56:32 -05:00
loop_state . subscriptions . remove_sub ( FileId ( file_id . 0 ) ) ;
2018-12-19 06:04:15 -06:00
}
2019-12-11 11:34:01 -06:00
let params =
2020-05-10 12:24:02 -05:00
lsp_types ::PublishDiagnosticsParams { uri , diagnostics : Vec ::new ( ) , version : None } ;
let not = notification_new ::< lsp_types ::notification ::PublishDiagnostics > ( params ) ;
2019-03-05 07:24:59 -06:00
msg_sender . send ( not . into ( ) ) . unwrap ( ) ;
2018-10-15 16:44:23 -05:00
return Ok ( ( ) ) ;
2018-09-01 09:40:45 -05:00
}
Err ( not ) = > not ,
} ;
2020-05-10 12:24:02 -05:00
let not = match notification_cast ::< lsp_types ::notification ::DidChangeConfiguration > ( not ) {
2020-03-20 17:01:47 -05:00
Ok ( _ ) = > {
// As stated in https://github.com/microsoft/language-server-protocol/issues/676,
// this notification's parameters should be ignored and the actual config queried separately.
2020-03-19 16:56:32 -05:00
let request_id = loop_state . next_request_id ( ) ;
2020-05-10 12:24:02 -05:00
let request = request_new ::< lsp_types ::request ::WorkspaceConfiguration > (
2020-03-19 16:56:32 -05:00
request_id . clone ( ) ,
2020-05-10 12:24:02 -05:00
lsp_types ::ConfigurationParams {
items : vec ! [ lsp_types ::ConfigurationItem {
2020-04-02 05:47:58 -05:00
scope_uri : None ,
section : Some ( " rust-analyzer " . to_string ( ) ) ,
} ] ,
} ,
2020-03-19 16:56:32 -05:00
) ;
2020-03-20 17:01:47 -05:00
msg_sender . send ( request . into ( ) ) ? ;
2020-03-21 17:40:07 -05:00
loop_state . configuration_request_id = Some ( request_id ) ;
2020-03-19 16:56:32 -05:00
2019-07-10 22:44:23 -05:00
return Ok ( ( ) ) ;
}
2020-03-19 16:56:32 -05:00
Err ( not ) = > not ,
2019-07-10 22:44:23 -05:00
} ;
2020-05-10 12:24:02 -05:00
let not = match notification_cast ::< lsp_types ::notification ::DidChangeWatchedFiles > ( not ) {
2019-09-06 08:25:24 -05:00
Ok ( params ) = > {
let mut vfs = state . vfs . write ( ) ;
for change in params . changes {
let uri = change . uri ;
let path = uri . to_file_path ( ) . map_err ( | ( ) | format! ( " invalid uri: {} " , uri ) ) ? ;
vfs . notify_changed ( path )
}
return Ok ( ( ) ) ;
}
Err ( not ) = > not ,
} ;
2020-03-09 03:26:41 -05:00
if not . method . starts_with ( " $/ " ) {
return Ok ( ( ) ) ;
}
2018-12-06 12:03:39 -06:00
log ::error! ( " unhandled notification: {:?} " , not ) ;
2018-08-12 16:09:30 -05:00
Ok ( ( ) )
}
2020-04-26 03:23:53 -05:00
fn apply_document_changes (
old_text : & mut String ,
mut line_index : Cow < '_ , LineIndex > ,
content_changes : Vec < TextDocumentContentChangeEvent > ,
) {
2020-05-03 04:46:20 -05:00
// Remove when https://github.com/rust-analyzer/rust-analyzer/issues/4263 is fixed.
let backup_text = old_text . clone ( ) ;
let backup_changes = content_changes . clone ( ) ;
2020-04-26 03:23:53 -05:00
// The changes we got must be applied sequentially, but can cross lines so we
// have to keep our line index updated.
// Some clients (e.g. Code) sort the ranges in reverse. As an optimization, we
// remember the last valid line in the index and only rebuild it if needed.
enum IndexValid {
All ,
2020-05-04 12:04:30 -05:00
UpToLineExclusive ( u64 ) ,
2020-04-26 03:23:53 -05:00
}
impl IndexValid {
fn covers ( & self , line : u64 ) -> bool {
match * self {
2020-05-04 12:04:30 -05:00
IndexValid ::UpToLineExclusive ( to ) = > to > line ,
2020-04-26 03:23:53 -05:00
_ = > true ,
}
}
}
let mut index_valid = IndexValid ::All ;
for change in content_changes {
match change . range {
Some ( range ) = > {
2020-05-04 12:04:30 -05:00
if ! index_valid . covers ( range . end . line ) {
2020-04-26 03:23:53 -05:00
line_index = Cow ::Owned ( LineIndex ::new ( & old_text ) ) ;
}
2020-05-04 12:04:30 -05:00
index_valid = IndexValid ::UpToLineExclusive ( range . start . line ) ;
2020-05-10 06:55:24 -05:00
let range = from_proto ::text_range ( & line_index , range ) ;
2020-05-03 04:46:20 -05:00
let mut text = old_text . to_owned ( ) ;
match std ::panic ::catch_unwind ( move | | {
text . replace_range ( Range ::< usize > ::from ( range ) , & change . text ) ;
text
} ) {
Ok ( t ) = > * old_text = t ,
Err ( e ) = > {
eprintln! ( " Bug in incremental text synchronization. Please report the following output on https://github.com/rust-analyzer/rust-analyzer/issues/4263 " ) ;
dbg! ( & backup_text ) ;
dbg! ( & backup_changes ) ;
std ::panic ::resume_unwind ( e ) ;
}
}
2020-04-26 03:23:53 -05:00
}
None = > {
* old_text = change . text ;
2020-05-04 12:04:30 -05:00
index_valid = IndexValid ::UpToLineExclusive ( 0 ) ;
2020-04-26 03:23:53 -05:00
}
}
}
}
2020-01-15 09:33:58 -06:00
fn on_check_task (
task : CheckTask ,
2020-06-03 04:16:08 -05:00
global_state : & mut GlobalState ,
2020-01-15 09:33:58 -06:00
task_sender : & Sender < Task > ,
) -> Result < ( ) > {
2020-01-31 12:23:25 -06:00
match task {
2020-01-15 09:33:58 -06:00
CheckTask ::ClearDiagnostics = > {
2020-01-31 12:23:25 -06:00
task_sender . send ( Task ::Diagnostic ( DiagnosticTask ::ClearCheck ) ) ? ;
2020-01-15 09:33:58 -06:00
}
2020-05-14 18:51:48 -05:00
CheckTask ::AddDiagnostic { workspace_root , diagnostic } = > {
let diagnostics = crate ::diagnostics ::to_proto ::map_rust_diagnostic_to_lsp (
& diagnostic ,
& workspace_root ,
) ;
for diag in diagnostics {
let path = diag
. location
. uri
. to_file_path ( )
. map_err ( | ( ) | format! ( " invalid uri: {} " , diag . location . uri ) ) ? ;
2020-06-03 04:16:08 -05:00
let file_id = match global_state . vfs . read ( ) . path2file ( & path ) {
2020-05-14 18:51:48 -05:00
Some ( file ) = > FileId ( file . 0 ) ,
None = > {
log ::error! (
" File with cargo diagnostic not found in VFS: {} " ,
path . display ( )
) ;
return Ok ( ( ) ) ;
}
} ;
2020-01-31 12:23:25 -06:00
2020-05-14 18:51:48 -05:00
task_sender . send ( Task ::Diagnostic ( DiagnosticTask ::AddCheck (
file_id ,
diag . diagnostic ,
diag . fixes . into_iter ( ) . map ( | it | it . into ( ) ) . collect ( ) ,
) ) ) ? ;
}
2020-01-15 09:33:58 -06:00
}
2020-05-14 18:51:48 -05:00
CheckTask ::Status ( status ) = > {
2020-06-03 04:16:08 -05:00
if global_state . config . client_caps . work_done_progress {
2020-05-14 18:51:48 -05:00
let progress = match status {
Status ::Being = > {
lsp_types ::WorkDoneProgress ::Begin ( lsp_types ::WorkDoneProgressBegin {
title : " Running `cargo check` " . to_string ( ) ,
cancellable : Some ( false ) ,
message : None ,
percentage : None ,
} )
}
Status ::Progress ( target ) = > {
lsp_types ::WorkDoneProgress ::Report ( lsp_types ::WorkDoneProgressReport {
cancellable : Some ( false ) ,
message : Some ( target ) ,
percentage : None ,
} )
}
Status ::End = > {
lsp_types ::WorkDoneProgress ::End ( lsp_types ::WorkDoneProgressEnd {
message : None ,
} )
}
} ;
2020-05-09 16:15:22 -05:00
let params = lsp_types ::ProgressParams {
token : lsp_types ::ProgressToken ::String (
" rustAnalyzer/cargoWatcher " . to_string ( ) ,
) ,
value : lsp_types ::ProgressParamsValue ::WorkDone ( progress ) ,
} ;
let not = notification_new ::< lsp_types ::notification ::Progress > ( params ) ;
task_sender . send ( Task ::Notify ( not ) ) . unwrap ( ) ;
}
2020-01-15 09:33:58 -06:00
}
2020-01-29 03:46:56 -06:00
} ;
2020-01-31 12:23:25 -06:00
Ok ( ( ) )
}
2020-01-29 03:46:56 -06:00
2020-06-03 04:16:08 -05:00
fn on_diagnostic_task ( task : DiagnosticTask , msg_sender : & Sender < Message > , state : & mut GlobalState ) {
2020-01-31 12:23:25 -06:00
let subscriptions = state . diagnostics . handle_task ( task ) ;
2020-01-15 09:33:58 -06:00
2020-01-31 12:23:25 -06:00
for file_id in subscriptions {
2020-06-13 04:00:06 -05:00
let url = file_id_to_url ( & state . vfs . read ( ) , file_id ) ;
2020-01-31 12:23:25 -06:00
let diagnostics = state . diagnostics . diagnostics_for ( file_id ) . cloned ( ) . collect ( ) ;
2020-06-13 04:00:06 -05:00
let params = lsp_types ::PublishDiagnosticsParams { uri : url , diagnostics , version : None } ;
2020-05-10 12:24:02 -05:00
let not = notification_new ::< lsp_types ::notification ::PublishDiagnostics > ( params ) ;
2020-01-31 12:23:25 -06:00
msg_sender . send ( not . into ( ) ) . unwrap ( ) ;
}
2020-01-15 09:33:58 -06:00
}
2020-03-28 17:33:16 -05:00
fn send_startup_progress ( sender : & Sender < Message > , loop_state : & mut LoopState ) {
let total : usize = loop_state . roots_total ;
2020-03-29 13:39:03 -05:00
let prev = loop_state . roots_progress_reported ;
let progress = loop_state . roots_scanned ;
loop_state . roots_progress_reported = Some ( progress ) ;
2020-03-13 18:03:02 -05:00
2020-03-29 13:39:03 -05:00
match ( prev , loop_state . workspace_loaded ) {
2020-03-16 01:32:28 -05:00
( None , false ) = > {
2020-05-10 12:24:02 -05:00
let work_done_progress_create = request_new ::< lsp_types ::request ::WorkDoneProgressCreate > (
2020-03-16 01:32:28 -05:00
loop_state . next_request_id ( ) ,
WorkDoneProgressCreateParams {
2020-05-10 12:24:02 -05:00
token : lsp_types ::ProgressToken ::String ( " rustAnalyzer/startup " . into ( ) ) ,
2020-03-16 01:32:28 -05:00
} ,
) ;
sender . send ( work_done_progress_create . into ( ) ) . unwrap ( ) ;
send_startup_progress_notif (
sender ,
WorkDoneProgress ::Begin ( WorkDoneProgressBegin {
title : " rust-analyzer " . into ( ) ,
cancellable : None ,
message : Some ( format! ( " {} / {} packages " , progress , total ) ) ,
percentage : Some ( 100.0 * progress as f64 / total as f64 ) ,
} ) ,
) ;
}
( Some ( prev ) , false ) if progress ! = prev = > send_startup_progress_notif (
2020-03-13 18:03:02 -05:00
sender ,
WorkDoneProgress ::Report ( WorkDoneProgressReport {
cancellable : None ,
message : Some ( format! ( " {} / {} packages " , progress , total ) ) ,
2020-03-13 18:27:39 -05:00
percentage : Some ( 100.0 * progress as f64 / total as f64 ) ,
2020-03-13 18:03:02 -05:00
} ) ,
2020-03-16 01:32:28 -05:00
) ,
( _ , true ) = > send_startup_progress_notif (
2020-03-15 23:44:27 -05:00
sender ,
WorkDoneProgress ::End ( WorkDoneProgressEnd {
message : Some ( format! ( " rust-analyzer loaded, {} packages " , progress ) ) ,
} ) ,
2020-03-16 01:32:28 -05:00
) ,
_ = > { }
2020-03-13 18:03:02 -05:00
}
2020-03-31 05:02:47 -05:00
fn send_startup_progress_notif ( sender : & Sender < Message > , work_done_progress : WorkDoneProgress ) {
2020-05-10 12:24:02 -05:00
let notif =
notification_new ::< lsp_types ::notification ::Progress > ( lsp_types ::ProgressParams {
token : lsp_types ::ProgressToken ::String ( " rustAnalyzer/startup " . into ( ) ) ,
value : lsp_types ::ProgressParamsValue ::WorkDone ( work_done_progress ) ,
} ) ;
2020-03-31 05:02:47 -05:00
sender . send ( notif . into ( ) ) . unwrap ( ) ;
}
2020-03-13 18:03:02 -05:00
}
2018-08-29 10:03:14 -05:00
struct PoolDispatcher < ' a > {
2019-08-30 09:24:11 -05:00
req : Option < Request > ,
2018-08-29 10:03:14 -05:00
pool : & ' a ThreadPool ,
2020-06-03 04:16:08 -05:00
global_state : & ' a mut GlobalState ,
2019-05-31 12:42:53 -05:00
pending_requests : & ' a mut PendingRequests ,
2019-08-30 09:24:11 -05:00
msg_sender : & ' a Sender < Message > ,
2020-01-29 04:15:08 -06:00
task_sender : & ' a Sender < Task > ,
2019-05-31 12:42:53 -05:00
request_received : Instant ,
2018-08-29 10:03:14 -05:00
}
impl < ' a > PoolDispatcher < ' a > {
2019-05-31 12:52:09 -05:00
/// Dispatches the request onto the current thread
2019-05-31 12:50:16 -05:00
fn on_sync < R > (
& mut self ,
2020-06-03 04:16:08 -05:00
f : fn ( & mut GlobalState , R ::Params ) -> Result < R ::Result > ,
2019-05-31 12:50:16 -05:00
) -> Result < & mut Self >
2018-10-15 16:44:23 -05:00
where
2020-05-10 12:24:02 -05:00
R : lsp_types ::request ::Request + 'static ,
2019-10-24 01:52:32 -05:00
R ::Params : DeserializeOwned + panic ::UnwindSafe + 'static ,
2018-10-15 16:44:23 -05:00
R ::Result : Serialize + 'static ,
2018-09-01 09:40:45 -05:00
{
2019-05-31 12:50:16 -05:00
let ( id , params ) = match self . parse ::< R > ( ) {
Some ( it ) = > it ,
None = > {
2019-05-31 12:23:56 -05:00
return Ok ( self ) ;
2018-09-01 09:40:45 -05:00
}
2019-05-31 12:23:56 -05:00
} ;
2020-06-03 04:16:08 -05:00
let world = panic ::AssertUnwindSafe ( & mut * self . global_state ) ;
2019-10-24 01:52:32 -05:00
let task = panic ::catch_unwind ( move | | {
let result = f ( world . 0 , params ) ;
result_to_task ::< R > ( id , result )
} )
. map_err ( | _ | format! ( " sync task {:?} panicked " , R ::METHOD ) ) ? ;
2020-06-03 04:16:08 -05:00
on_task ( task , self . msg_sender , self . pending_requests , self . global_state ) ;
2019-05-31 12:50:16 -05:00
Ok ( self )
}
2019-05-31 12:23:56 -05:00
2019-05-31 12:52:09 -05:00
/// Dispatches the request onto thread pool
2020-06-03 04:16:08 -05:00
fn on < R > (
& mut self ,
f : fn ( GlobalStateSnapshot , R ::Params ) -> Result < R ::Result > ,
) -> Result < & mut Self >
2019-05-31 12:50:16 -05:00
where
2020-05-10 12:24:02 -05:00
R : lsp_types ::request ::Request + 'static ,
2019-05-31 12:50:16 -05:00
R ::Params : DeserializeOwned + Send + 'static ,
R ::Result : Serialize + 'static ,
{
let ( id , params ) = match self . parse ::< R > ( ) {
Some ( it ) = > it ,
None = > {
return Ok ( self ) ;
}
} ;
2019-05-31 12:23:56 -05:00
2019-05-31 12:30:14 -05:00
self . pool . execute ( {
2020-06-03 04:16:08 -05:00
let world = self . global_state . snapshot ( ) ;
2020-01-29 04:15:08 -06:00
let sender = self . task_sender . clone ( ) ;
2019-05-31 12:30:14 -05:00
move | | {
let result = f ( world , params ) ;
let task = result_to_task ::< R > ( id , result ) ;
sender . send ( task ) . unwrap ( ) ;
}
2019-05-31 12:23:56 -05:00
} ) ;
2019-05-31 12:30:14 -05:00
2018-08-29 10:03:14 -05:00
Ok ( self )
}
2018-08-31 04:04:33 -05:00
2019-08-30 09:24:11 -05:00
fn parse < R > ( & mut self ) -> Option < ( RequestId , R ::Params ) >
2019-05-31 12:50:16 -05:00
where
2020-05-10 12:24:02 -05:00
R : lsp_types ::request ::Request + 'static ,
2019-10-24 01:52:32 -05:00
R ::Params : DeserializeOwned + 'static ,
2019-05-31 12:50:16 -05:00
{
let req = self . req . take ( ) ? ;
2019-08-30 09:24:11 -05:00
let ( id , params ) = match req . extract ::< R ::Params > ( R ::METHOD ) {
2019-05-31 12:50:16 -05:00
Ok ( it ) = > it ,
Err ( req ) = > {
self . req = Some ( req ) ;
return None ;
}
} ;
self . pending_requests . start ( PendingRequest {
2019-08-30 09:24:11 -05:00
id : id . clone ( ) ,
2019-05-31 12:50:16 -05:00
method : R ::METHOD . to_string ( ) ,
received : self . request_received ,
} ) ;
Some ( ( id , params ) )
}
2019-05-31 12:42:53 -05:00
fn finish ( & mut self ) {
match self . req . take ( ) {
None = > ( ) ,
Some ( req ) = > {
log ::error! ( " unknown request: {:?} " , req ) ;
2019-08-30 09:24:11 -05:00
let resp = Response ::new_err (
2019-05-31 12:42:53 -05:00
req . id ,
ErrorCode ::MethodNotFound as i32 ,
" unknown request " . to_string ( ) ,
) ;
self . msg_sender . send ( resp . into ( ) ) . unwrap ( ) ;
}
2018-08-31 04:04:33 -05:00
}
}
2018-08-12 14:08:14 -05:00
}
2019-08-30 09:24:11 -05:00
fn result_to_task < R > ( id : RequestId , result : Result < R ::Result > ) -> Task
2019-05-31 12:30:14 -05:00
where
2020-05-10 12:24:02 -05:00
R : lsp_types ::request ::Request + 'static ,
2019-10-24 01:52:32 -05:00
R ::Params : DeserializeOwned + 'static ,
2019-05-31 12:30:14 -05:00
R ::Result : Serialize + 'static ,
{
let response = match result {
2019-08-30 09:24:11 -05:00
Ok ( resp ) = > Response ::new_ok ( id , & resp ) ,
2019-05-31 12:30:14 -05:00
Err ( e ) = > match e . downcast ::< LspError > ( ) {
2020-03-09 04:54:14 -05:00
Ok ( lsp_error ) = > {
if lsp_error . code = = LspError ::UNKNOWN_FILE {
// Work-around for https://github.com/rust-analyzer/rust-analyzer/issues/1521
Response ::new_ok ( id , ( ) )
} else {
Response ::new_err ( id , lsp_error . code , lsp_error . message )
}
}
2019-05-31 12:30:14 -05:00
Err ( e ) = > {
if is_canceled ( & e ) {
2019-12-30 15:18:16 -06:00
Response ::new_err (
id ,
ErrorCode ::ContentModified as i32 ,
" content modified " . to_string ( ) ,
)
2019-05-31 12:30:14 -05:00
} else {
2019-08-30 09:24:11 -05:00
Response ::new_err ( id , ErrorCode ::InternalError as i32 , e . to_string ( ) )
2019-05-31 12:30:14 -05:00
}
}
} ,
} ;
Task ::Respond ( response )
}
2018-08-12 14:08:14 -05:00
fn update_file_notifications_on_threadpool (
pool : & ThreadPool ,
2020-06-03 04:16:08 -05:00
world : GlobalStateSnapshot ,
2020-01-29 04:15:08 -06:00
task_sender : Sender < Task > ,
2018-08-30 08:27:09 -05:00
subscriptions : Vec < FileId > ,
2018-08-12 14:08:14 -05:00
) {
2019-08-21 09:30:58 -05:00
log ::trace! ( " updating notifications for {:?} " , subscriptions ) ;
2020-04-02 05:50:34 -05:00
if world . config . publish_diagnostics {
pool . execute ( move | | {
for file_id in subscriptions {
2019-08-22 06:44:16 -05:00
match handlers ::publish_diagnostics ( & world , file_id ) {
Err ( e ) = > {
if ! is_canceled ( & e ) {
log ::error! ( " failed to compute diagnostics: {:?} " , e ) ;
}
}
2020-01-31 12:23:25 -06:00
Ok ( task ) = > {
task_sender . send ( Task ::Diagnostic ( task ) ) . unwrap ( ) ;
2018-10-25 08:03:49 -05:00
}
2018-08-30 08:27:09 -05:00
}
2018-08-12 14:08:14 -05:00
}
2020-04-02 05:50:34 -05:00
} )
}
2018-08-12 14:08:14 -05:00
}
2018-09-03 15:32:42 -05:00
2020-05-10 12:24:02 -05:00
pub fn show_message (
typ : lsp_types ::MessageType ,
message : impl Into < String > ,
sender : & Sender < Message > ,
) {
2019-03-07 08:46:17 -06:00
let message = message . into ( ) ;
2020-05-10 12:24:02 -05:00
let params = lsp_types ::ShowMessageParams { typ , message } ;
let not = notification_new ::< lsp_types ::notification ::ShowMessage > ( params ) ;
2019-03-05 07:24:59 -06:00
sender . send ( not . into ( ) ) . unwrap ( ) ;
2018-09-03 15:32:42 -05:00
}
2018-10-25 08:03:49 -05:00
2019-06-14 15:42:56 -05:00
fn is_canceled ( e : & Box < dyn std ::error ::Error + Send + Sync > ) -> bool {
2018-10-25 08:03:49 -05:00
e . downcast_ref ::< Canceled > ( ) . is_some ( )
}
2019-08-30 09:24:11 -05:00
fn notification_is < N : lsp_types ::notification ::Notification > ( notification : & Notification ) -> bool {
notification . method = = N ::METHOD
}
fn notification_cast < N > ( notification : Notification ) -> std ::result ::Result < N ::Params , Notification >
where
N : lsp_types ::notification ::Notification ,
N ::Params : DeserializeOwned ,
{
notification . extract ( N ::METHOD )
}
fn notification_new < N > ( params : N ::Params ) -> Notification
where
N : lsp_types ::notification ::Notification ,
N ::Params : Serialize ,
{
Notification ::new ( N ::METHOD . to_string ( ) , params )
}
2019-09-06 08:25:24 -05:00
fn request_new < R > ( id : RequestId , params : R ::Params ) -> Request
where
R : lsp_types ::request ::Request ,
R ::Params : Serialize ,
{
Request ::new ( id , R ::METHOD . to_string ( ) , params )
}
2020-04-26 03:23:53 -05:00
#[ cfg(test) ]
mod tests {
use std ::borrow ::Cow ;
use lsp_types ::{ Position , Range , TextDocumentContentChangeEvent } ;
use ra_ide ::LineIndex ;
#[ test ]
fn apply_document_changes ( ) {
fn run ( text : & mut String , changes : Vec < TextDocumentContentChangeEvent > ) {
let line_index = Cow ::Owned ( LineIndex ::new ( & text ) ) ;
super ::apply_document_changes ( text , line_index , changes ) ;
}
macro_rules ! c {
[ $( $sl :expr , $sc :expr ; $el :expr , $ec :expr = > $text :expr ) , + ] = > {
vec! [ $( TextDocumentContentChangeEvent {
range : Some ( Range {
start : Position { line : $sl , character : $sc } ,
end : Position { line : $el , character : $ec } ,
} ) ,
range_length : None ,
text : String ::from ( $text ) ,
} ) , + ]
} ;
}
let mut text = String ::new ( ) ;
run ( & mut text , vec! [ ] ) ;
assert_eq! ( text , " " ) ;
run (
& mut text ,
vec! [ TextDocumentContentChangeEvent {
range : None ,
range_length : None ,
text : String ::from ( " the " ) ,
} ] ,
) ;
assert_eq! ( text , " the " ) ;
run ( & mut text , c! [ 0 , 3 ; 0 , 3 = > " quick " ] ) ;
assert_eq! ( text , " the quick " ) ;
run ( & mut text , c! [ 0 , 0 ; 0 , 4 = > " " , 0 , 5 ; 0 , 5 = > " foxes " ] ) ;
assert_eq! ( text , " quick foxes " ) ;
run ( & mut text , c! [ 0 , 11 ; 0 , 11 = > " \n dream " ] ) ;
assert_eq! ( text , " quick foxes \n dream " ) ;
run ( & mut text , c! [ 1 , 0 ; 1 , 0 = > " have " ] ) ;
assert_eq! ( text , " quick foxes \n have dream " ) ;
run ( & mut text , c! [ 0 , 0 ; 0 , 0 = > " the " , 1 , 4 ; 1 , 4 = > " quiet " , 1 , 16 ; 1 , 16 = > " s \n " ] ) ;
assert_eq! ( text , " the quick foxes \n have quiet dreams \n " ) ;
run ( & mut text , c! [ 0 , 15 ; 0 , 15 = > " \n " , 2 , 17 ; 2 , 17 = > " \n " ] ) ;
assert_eq! ( text , " the quick foxes \n \n have quiet dreams \n \n " ) ;
run (
& mut text ,
c! [ 1 , 0 ; 1 , 0 = > " DREAM " , 2 , 0 ; 2 , 0 = > " they " , 3 , 0 ; 3 , 0 = > " DON'T THEY? " ] ,
) ;
assert_eq! ( text , " the quick foxes \n DREAM \n they have quiet dreams \n DON'T THEY? \n " ) ;
run ( & mut text , c! [ 0 , 10 ; 1 , 5 = > " " , 2 , 0 ; 2 , 12 = > " " ] ) ;
assert_eq! ( text , " the quick \n they have quiet dreams \n " ) ;
2020-05-05 11:22:01 -05:00
text = String ::from ( " ❤️ " ) ;
run ( & mut text , c! [ 0 , 0 ; 0 , 0 = > " a " ] ) ;
assert_eq! ( text , " a❤️ " ) ;
text = String ::from ( " a \n b " ) ;
run ( & mut text , c! [ 0 , 1 ; 1 , 0 = > " \n țc " , 0 , 1 ; 1 , 1 = > " d " ] ) ;
assert_eq! ( text , " adcb " ) ;
text = String ::from ( " a \n b " ) ;
run ( & mut text , c! [ 0 , 1 ; 1 , 0 = > " ț \n c " , 0 , 2 ; 0 , 2 = > " c " ] ) ;
assert_eq! ( text , " ațc \n cb " ) ;
2020-04-26 03:23:53 -05:00
}
}