1- use std:: { fs, path:: Path } ;
2-
31use anyhow:: { Result , anyhow, bail} ;
42use crossbeam_channel:: { Receiver , Sender } ;
53use linked_hash_map:: LinkedHashMap ;
64use lsp_server:: { IoThreads , Message } ;
7- use lsp_types:: { CompletionItem , CompletionOptions , InitializeParams , ServerCapabilities , request:: { self , Request } } ;
5+ use lsp_types:: { CompletionItem , CompletionOptions , InitializeParams , MessageType , Position , ServerCapabilities , ShowMessageParams , TextDocumentSyncCapability , TextDocumentSyncKind , Uri , notification :: { self , Notification } , request:: { self , Request } } ;
86use syntax:: { Compile , CompileMeta , EmulateInfo , Expand , LSP_DEBUG } ;
97
108fn main ( ) {
119 main_loop ( ) . unwrap ( ) ;
1210}
1311
12+ fn lopos ( pos : Position ) -> ( u32 , u32 ) {
13+ ( pos. line + 1 , pos. character + 1 )
14+ }
15+
1416struct IoJoiner ( pub Option < IoThreads > ) ;
1517impl std:: ops:: DerefMut for IoJoiner {
1618 fn deref_mut ( & mut self ) -> & mut Self :: Target {
@@ -45,6 +47,7 @@ fn main_loop() -> Result<()> {
4547 trigger_characters : Some ( vec ! [ ] ) ,
4648 ..Default :: default ( )
4749 } ) ,
50+ text_document_sync : Some ( TextDocumentSyncCapability :: Kind ( TextDocumentSyncKind :: FULL ) ) ,
4851 ..Default :: default ( )
4952 } ;
5053 let server_capabilities = serde_json:: to_value ( server_capabilities) ?;
@@ -55,33 +58,60 @@ fn main_loop() -> Result<()> {
5558 } = serde_json:: from_value :: < InitializeParams > ( init_params) ?;
5659 let _workspace_folders = workspace_folders. ok_or ( anyhow ! ( "Cannot find workspace folder" ) ) ?;
5760 let mut ctx = Ctx {
61+ open_files : Default :: default ( ) ,
5862 sender : connect. sender ,
5963 recver : connect. receiver ,
6064 } ;
6165 ctx. run ( )
6266}
6367
6468struct Ctx {
69+ open_files : LinkedHashMap < Uri , String > ,
6570 sender : Sender < Message > ,
6671 recver : Receiver < Message > ,
6772}
6873impl Ctx {
6974 fn run ( & mut self ) -> Result < ( ) , anyhow:: Error > {
7075 while let Ok ( msg) = self . recver . recv ( ) {
7176 match msg {
72- Message :: Request ( request) => self . handle_request ( request) ?,
77+ Message :: Request ( request) => self . handle_requests ( request) ?,
7378 Message :: Response ( response) => {
7479 eprintln ! ( "unknown response {response:?}" )
7580 } ,
7681 Message :: Notification ( notification) => {
82+ let notification = & mut Some ( notification) ;
83+ self . try_handle_notif :: < notification:: DidOpenTextDocument > ( notification) ?;
84+ self . try_handle_notif :: < notification:: DidChangeTextDocument > ( notification) ?;
85+ self . try_handle_notif :: < notification:: DidCloseTextDocument > ( notification) ?;
7786 eprintln ! ( "unknown notification {notification:?}" )
7887 } ,
7988 }
8089 }
8190 Ok ( ( ) )
8291 }
8392
84- fn try_handle < T : RequestHandler > ( & mut self , request : & mut Option < lsp_server:: Request > ) -> Option < Result < ( ) > > {
93+ fn handle_requests ( & mut self , request : lsp_server:: Request ) -> Result < ( ) > {
94+ let request = & mut Some ( request) ;
95+
96+ self . try_handle_req :: < request:: Completion > ( request) . transpose ( ) ?;
97+
98+ if let Some ( request) = request {
99+ bail ! ( "unknown request {request:#?}" )
100+ }
101+ Ok ( ( ) )
102+ }
103+
104+ fn try_handle_notif < T : NotificationHandler > ( & mut self , notification : & mut Option < lsp_server:: Notification > ) -> Result < ( ) > {
105+ let Some ( lsp_server:: Notification { params, .. } )
106+ = notification. take_if ( |it| it. method == T :: METHOD ) else { return Ok ( ( ) ) } ;
107+ let params = match serde_json:: from_value ( params) {
108+ Ok ( it) => it,
109+ Err ( err) => bail ! ( err) ,
110+ } ;
111+ T :: handle ( self , params)
112+ }
113+
114+ fn try_handle_req < T : RequestHandler > ( & mut self , request : & mut Option < lsp_server:: Request > ) -> Option < Result < ( ) > > {
85115 let lsp_server:: Request { id, params, .. }
86116 = request. take_if ( |req| req. method == T :: METHOD ) ?;
87117 let params = match serde_json:: from_value ( params) {
@@ -109,21 +139,15 @@ impl Ctx {
109139 Some ( self . sender . send ( Message :: Response ( response) ) . map_err ( Into :: into) )
110140 }
111141
112- fn handle_request ( & mut self , request : lsp_server:: Request ) -> Result < ( ) > {
113- let request = & mut Some ( request) ;
114-
115- None . or_else ( || self . try_handle :: < request:: Completion > ( request) )
116- . unwrap_or_else ( || bail ! ( "unknown request {request:#?}" ) )
117- }
118-
119- fn read_file ( & self , path : impl AsRef < Path > ) -> Result < String > {
120- let file = fs:: read ( path) ?;
121- let file = String :: from_utf8_lossy ( & file) ;
122- Ok ( file. into_owned ( ) )
142+ fn read_file ( & self , uri : & Uri ) -> Result < & str > {
143+ match self . open_files . get ( uri) {
144+ Some ( s) => Ok ( s) ,
145+ None => bail ! ( "Cannot read no opened file" ) ,
146+ }
123147 }
124148
125149 fn try_parse ( & self , ( line, col) : ( u32 , u32 ) , file : & str ) -> Result < ( Expand , String ) > {
126- let index = line_column:: index ( & file, line+ 1 , col+ 1 ) ;
150+ let index = line_column:: index ( & file, line, col) ;
127151 let placeholders = [
128152 format ! ( "{LSP_DEBUG} " ) ,
129153 format ! ( "{LSP_DEBUG} __lsp_arg;" ) ,
@@ -141,18 +165,31 @@ impl Ctx {
141165 }
142166 Err ( anyhow ! ( "Fake parse err: {parse_err}" ) )
143167 }
168+
169+ fn send_window_notif ( & self , typ : MessageType , msg : impl std:: fmt:: Display ) -> Result < ( ) > {
170+ let params = ShowMessageParams {
171+ typ,
172+ message : msg. to_string ( ) ,
173+ } ;
174+ let params = serde_json:: to_value ( params) ?;
175+ let msg = Message :: Notification ( lsp_server:: Notification {
176+ method : notification:: ShowMessage :: METHOD . to_owned ( ) ,
177+ params,
178+ } ) ;
179+ self . sender . send ( msg) ?;
180+ Ok ( ( ) )
181+ }
144182}
145183
146184trait RequestHandler : Request {
147185 fn handle ( ctx : & Ctx , param : Self :: Params ) -> Result < Self :: Result > ;
148186}
149187impl RequestHandler for request:: Completion {
150188 fn handle ( ctx : & Ctx , param : Self :: Params ) -> Result < Self :: Result > {
151- let path = param. text_document_position . text_document . uri . path ( ) ;
152- let line = param. text_document_position . position . line ;
153- let col = param. text_document_position . position . character ;
189+ let uri = param. text_document_position . text_document . uri ;
190+ let ( line, col) = lopos ( param. text_document_position . position ) ;
154191
155- let file = ctx. read_file ( path . as_str ( ) ) ?;
192+ let file = ctx. read_file ( & uri ) ?;
156193 let ( top, src) = ctx. try_parse ( ( line, col) , & file) ?;
157194 let mut meta = CompileMeta :: with_source ( src. into ( ) ) ;
158195 meta. is_emulated = true ;
@@ -196,3 +233,37 @@ fn generate_completes(infos: &[EmulateInfo]) -> Vec<CompletionItem> {
196233 }
197234 } ) . collect ( )
198235}
236+
237+ trait NotificationHandler : Notification {
238+ fn handle ( ctx : & mut Ctx , param : Self :: Params ) -> Result < ( ) > ;
239+ }
240+ impl NotificationHandler for notification:: DidOpenTextDocument {
241+ fn handle ( ctx : & mut Ctx , param : Self :: Params ) -> Result < ( ) > {
242+ ctx. send_window_notif ( MessageType :: LOG , "open file" ) ?;
243+ let file = ctx. open_files . entry ( param. text_document . uri ) . or_default ( ) ;
244+ * file = param. text_document . text ;
245+ Ok ( ( ) )
246+ }
247+ }
248+ impl NotificationHandler for notification:: DidChangeTextDocument {
249+ fn handle ( ctx : & mut Ctx , param : Self :: Params ) -> Result < ( ) > {
250+ let file = ctx. open_files . entry ( param. text_document . uri ) . or_default ( ) ;
251+
252+ for change in param. content_changes {
253+ if change. range . is_some ( ) {
254+ bail ! ( "unsupported range change sync: {change:#?}" )
255+ }
256+ * file = change. text ;
257+ }
258+ Ok ( ( ) )
259+ }
260+ }
261+ impl NotificationHandler for notification:: DidCloseTextDocument {
262+ fn handle ( ctx : & mut Ctx , param : Self :: Params ) -> Result < ( ) > {
263+ let uri = param. text_document . uri ;
264+ if ctx. open_files . remove ( & uri) . is_none ( ) {
265+ ctx. send_window_notif ( MessageType :: WARNING , format_args ! ( "Cannot close unknown file: {uri:?}" ) ) ?;
266+ }
267+ Ok ( ( ) )
268+ }
269+ }
0 commit comments