@@ -165,6 +165,19 @@ defmodule GenLSP do
165165 """
166166 @ callback handle_info ( message :: any ( ) , state ) :: { :noreply , state } when state: GenLSP.LSP . t ( )
167167
168+ @ default_sync_notifications [
169+ GenLSP.Notifications.TextDocumentDidOpen ,
170+ GenLSP.Notifications.TextDocumentDidChange ,
171+ GenLSP.Notifications.TextDocumentDidClose ,
172+ GenLSP.Notifications.TextDocumentDidSave ,
173+ GenLSP.Notifications.TextDocumentWillSave ,
174+ GenLSP.Notifications.WorkspaceDidChangeWatchedFiles ,
175+ GenLSP.Notifications.WorkspaceDidChangeWorkspaceFolders ,
176+ GenLSP.Notifications.WorkspaceDidChangeConfiguration ,
177+ GenLSP.Notifications.Initialized ,
178+ GenLSP.Notifications.Exit
179+ ]
180+
168181 @ options_schema NimbleOptions . new! (
169182 buffer: [
170183 type: { :or , [ :pid , :atom ] } ,
@@ -182,6 +195,11 @@ defmodule GenLSP do
182195 type: :atom ,
183196 doc:
184197 "Used for name registration as described in the \" Name registration\" section in the documentation for `GenServer`."
198+ ] ,
199+ sync_notifications: [
200+ type: { :list , :atom } ,
201+ doc:
202+ "List of notification to process synchronously. Defaults to document lifecycle notifications."
185203 ]
186204 )
187205
@@ -196,7 +214,8 @@ defmodule GenLSP do
196214 opts = NimbleOptions . validate! ( opts , @ options_schema )
197215
198216 :proc_lib . start_link ( __MODULE__ , :init , [
199- { module , init_args , Keyword . take ( opts , [ :name , :buffer , :assigns , :task_supervisor ] ) ,
217+ { module , init_args ,
218+ Keyword . take ( opts , [ :name , :buffer , :assigns , :task_supervisor , :sync_notifications ] ) ,
200219 self ( ) }
201220 ] )
202221 end
@@ -207,14 +226,16 @@ defmodule GenLSP do
207226 buffer = opts [ :buffer ]
208227 assigns = opts [ :assigns ]
209228 task_supervisor = opts [ :task_supervisor ]
229+ sync_notifications = opts [ :sync_notifications ] || @ default_sync_notifications
210230
211231 lsp = % LSP {
212232 mod: module ,
213233 pid: me ,
214234 buffer: buffer ,
215235 assigns: assigns ,
216236 task_supervisor: task_supervisor ,
217- tasks: Map . new ( )
237+ tasks: Map . new ( ) ,
238+ sync_notifications: MapSet . new ( sync_notifications )
218239 }
219240
220241 case module . init ( lsp , init_args ) do
@@ -472,76 +493,22 @@ defmodule GenLSP do
472493 start = System . system_time ( :microsecond )
473494 :telemetry . execute ( [ :gen_lsp , :notification , :client , :start ] , % { } )
474495
475- attempt (
476- lsp ,
477- "Last message received: handle_notification #{ inspect ( notification ) } " ,
478- [ :gen_lsp , :notification , :client ] ,
479- fn
480- { :error , _ } ->
481- Logger . warning ( "client -> server notification crashed" )
482-
483- _ ->
484- case GenLSP.Notifications . new ( notification ) do
485- { :ok , % GenLSP.Notifications.DollarCancelRequest { } = note } ->
486- result =
487- :telemetry . span (
488- [ :gen_lsp , :handle_notification ] ,
489- % { method: note . method } ,
490- fn ->
491- with pid when is_pid ( pid ) <- lsp . tasks [ note . params . id ] do
492- Task.Supervisor . terminate_child ( lsp . task_supervisor , pid )
493- end
494-
495- { { :noreply , lsp } , % { } }
496- end
497- )
498-
499- case result do
500- { :noreply , % LSP { } } ->
501- duration = System . system_time ( :microsecond ) - start
502-
503- Logger . debug (
504- "handled notification client -> server #{ note . method } in #{ format_time ( duration ) } " ,
505- method: note . method
506- )
507-
508- :telemetry . execute ( [ :gen_lsp , :notification , :client , :stop ] , % {
509- duration: duration
510- } )
511- end
512-
513- { :ok , note } ->
514- result =
515- :telemetry . span (
516- [ :gen_lsp , :handle_notification ] ,
517- % { method: note . method } ,
518- fn ->
519- { lsp . mod . handle_notification ( note , lsp ) , % { } }
520- end
521- )
522-
523- case result do
524- { :noreply , % LSP { } } ->
525- duration = System . system_time ( :microsecond ) - start
526-
527- Logger . debug (
528- "handled notification client -> server #{ note . method } in #{ format_time ( duration ) } " ,
529- method: note . method
530- )
531-
532- :telemetry . execute ( [ :gen_lsp , :notification , :client , :stop ] , % {
533- duration: duration
534- } )
535- end
496+ case GenLSP.Notifications . new ( notification ) do
497+ { :ok , % GenLSP.Notifications.DollarCancelRequest { } = note } ->
498+ handle_cancel_request ( lsp , note , start )
536499
537- { :error , errors } ->
538- # the payload is not parseable at all, other than being valid JSON
539- exception = InvalidNotification . exception ( { notification , errors } )
500+ { :ok , note } ->
501+ if MapSet . member? ( lsp . sync_notifications , note . __struct__ ) do
502+ handle_notification_sync ( lsp , note , start )
503+ else
504+ handle_notification_async ( lsp , note , start )
505+ end
540506
541- Logger . warning ( Exception . format ( :error , exception ) )
542- end
543- end
544- )
507+ { :error , errors } ->
508+ # the payload is not parseable at all, other than being valid JSON
509+ exception = InvalidNotification . exception ( { notification , errors } )
510+ Logger . warning ( Exception . format ( :error , exception ) )
511+ end
545512
546513 loop ( lsp , parent , deb )
547514
@@ -603,6 +570,89 @@ defmodule GenLSP do
603570 end )
604571 end
605572
573+ defp handle_cancel_request ( lsp , note , start ) do
574+ result =
575+ :telemetry . span ( [ :gen_lsp , :handle_notification ] , % { method: note . method } , fn ->
576+ with pid when is_pid ( pid ) <- lsp . tasks [ note . params . id ] do
577+ Task.Supervisor . terminate_child ( lsp . task_supervisor , pid )
578+ end
579+
580+ { { :noreply , lsp } , % { } }
581+ end )
582+
583+ case result do
584+ { :noreply , % LSP { } } ->
585+ duration = System . system_time ( :microsecond ) - start
586+
587+ Logger . debug (
588+ "handled notification client -> server #{ note . method } in #{ format_time ( duration ) } " ,
589+ method: note . method
590+ )
591+
592+ :telemetry . execute ( [ :gen_lsp , :notification , :client , :stop ] , % { duration: duration } )
593+ end
594+ end
595+
596+ defp handle_notification_sync ( lsp , note , start ) do
597+ try do
598+ result =
599+ :telemetry . span ( [ :gen_lsp , :handle_notification ] , % { method: note . method } , fn ->
600+ { lsp . mod . handle_notification ( note , lsp ) , % { } }
601+ end )
602+
603+ case result do
604+ { :noreply , % LSP { } } ->
605+ duration = System . system_time ( :microsecond ) - start
606+
607+ Logger . debug (
608+ "handled notification client -> server #{ note . method } in #{ format_time ( duration ) } " ,
609+ method: note . method
610+ )
611+
612+ :telemetry . execute ( [ :gen_lsp , :notification , :client , :stop ] , % { duration: duration } )
613+ end
614+ rescue
615+ e ->
616+ :telemetry . execute ( [ :gen_lsp , :notification , :client , :exception ] , % {
617+ message: "Last message received: handle_notification #{ inspect ( note ) } "
618+ } )
619+
620+ message = Exception . format ( :error , e , __STACKTRACE__ )
621+ Logger . error ( message )
622+ error ( lsp , message )
623+ end
624+ end
625+
626+ defp handle_notification_async ( lsp , note , start ) do
627+ attempt (
628+ lsp ,
629+ "Last message received: handle_notification #{ inspect ( note ) } " ,
630+ [ :gen_lsp , :notification , :client ] ,
631+ fn
632+ { :error , _ } ->
633+ :ok
634+
635+ _ ->
636+ result =
637+ :telemetry . span ( [ :gen_lsp , :handle_notification ] , % { method: note . method } , fn ->
638+ { lsp . mod . handle_notification ( note , lsp ) , % { } }
639+ end )
640+
641+ case result do
642+ { :noreply , % LSP { } } ->
643+ duration = System . system_time ( :microsecond ) - start
644+
645+ Logger . debug (
646+ "handled notification client -> server #{ note . method } in #{ format_time ( duration ) } " ,
647+ method: note . method
648+ )
649+
650+ :telemetry . execute ( [ :gen_lsp , :notification , :client , :stop ] , % { duration: duration } )
651+ end
652+ end
653+ )
654+ end
655+
606656 defp dump! ( schematic , structure ) do
607657 { :ok , output } = Schematic . dump ( schematic , structure )
608658 output
0 commit comments