11defmodule Expert do
2+ alias Expert.Project
23 alias Expert.Protocol.Convert
34 alias Expert.Protocol.Id
45 alias Expert.Provider.Handlers
56 alias Expert.State
7+ alias GenLSP.Enumerations
68 alias GenLSP.Requests
79 alias GenLSP.Structures
810
@@ -51,19 +53,33 @@ defmodule Expert do
5153
5254 def handle_request ( % GenLSP.Requests.Initialize { } = request , lsp ) do
5355 state = assigns ( lsp ) . state
54- Process . send_after ( self ( ) , :default_config , :timer . seconds ( 5 ) )
5556
56- case State . initialize ( state , request ) do
57- { :ok , response , state } ->
58- lsp = assign ( lsp , state: state )
59- { :ok , response } = Expert.Protocol.Convert . to_lsp ( response )
57+ with { :ok , response , state } <- State . initialize ( state , request ) ,
58+ { :ok , response } <- Expert.Protocol.Convert . to_lsp ( response ) do
59+ Task.Supervisor . start_child ( :expert_task_queue , fn ->
60+ config = state . configuration
6061
61- { :reply , response , lsp }
62+ log_info ( lsp , "Starting project" )
63+
64+ start_result = Project.Supervisor . start ( config . project )
6265
63- { :error , error } ->
66+ send ( Expert , { :engine_initialized , start_result } )
67+ end )
68+
69+ { :reply , response , assign ( lsp , state: state ) }
70+ else
71+ { :error , :already_initialized } ->
6472 response = % GenLSP.ErrorResponse {
6573 code: GenLSP.Enumerations.ErrorCodes . invalid_request ( ) ,
66- message: to_string ( error )
74+ message: "Already initialized"
75+ }
76+
77+ { :reply , response , lsp }
78+
79+ { :error , reason } ->
80+ response = % GenLSP.ErrorResponse {
81+ code: GenLSP.Enumerations.ErrorCodes . server_not_initialized ( ) ,
82+ message: "Failed to initialize: #{ inspect ( reason ) } "
6783 }
6884
6985 { :reply , response , lsp }
@@ -173,7 +189,7 @@ defmodule Expert do
173189 end
174190 end
175191
176- def handle_info ( :engine_initialized , lsp ) do
192+ def handle_info ( { :engine_initialized , { :ok , _pid } } , lsp ) do
177193 state = assigns ( lsp ) . state
178194
179195 new_state = % { state | engine_initialized?: true }
@@ -185,20 +201,33 @@ defmodule Expert do
185201 { :noreply , lsp }
186202 end
187203
188- def handle_info ( :default_config , lsp ) do
189- state = assigns ( lsp ) . state
204+ def handle_info ( { :engine_initialized , { :error , reason } } , lsp ) do
205+ error_message = initialization_error_message ( reason )
206+ log_error ( lsp , error_message )
190207
191- if state . configuration == nil do
192- Logger . warning (
193- "Did not receive workspace/didChangeConfiguration notification after 5 seconds. " <>
194- "Using default settings."
195- )
208+ { :noreply , lsp }
209+ end
196210
197- { :ok , config } = State . default_configuration ( state )
198- { :noreply , assign ( lsp , state: % { state | configuration: config } ) }
199- else
200- { :noreply , lsp }
201- end
211+ def log_info ( lsp \\ get_lsp ( ) , message ) do
212+ message = log_prepend_project_root ( message , assigns ( lsp ) . state )
213+
214+ Logger . info ( message )
215+ GenLSP . info ( lsp , message )
216+ end
217+
218+ # When logging errors we also notify the client to display the message
219+ def log_error ( lsp \\ get_lsp ( ) , message ) do
220+ message = log_prepend_project_root ( message , assigns ( lsp ) . state )
221+
222+ Logger . error ( message )
223+ GenLSP . error ( lsp , message )
224+
225+ GenLSP . notify ( lsp , % GenLSP.Notifications.WindowShowMessage {
226+ params: % GenLSP.Structures.ShowMessageParams {
227+ type: Enumerations.MessageType . error ( ) ,
228+ message: message
229+ }
230+ } )
202231 end
203232
204233 defp apply_to_state ( % State { } = state , % { } = request_or_notification ) do
@@ -273,4 +302,41 @@ defmodule Expert do
273302 register_options: % Structures.DidChangeWatchedFilesRegistrationOptions { watchers: watchers }
274303 }
275304 end
305+
306+ defp initialization_error_message ( { :shutdown , { :failed_to_start_child , child , { reason , _ } } } ) do
307+ case child do
308+ { Project.Node , node_name } ->
309+ node_initialization_message ( node_name , reason )
310+
311+ child ->
312+ "Failed to start child #{ inspect ( child ) } : #{ inspect ( reason ) } "
313+ end
314+ end
315+
316+ defp initialization_error_message ( reason ) do
317+ "Failed to initialize: #{ inspect ( reason ) } "
318+ end
319+
320+ defp node_initialization_message ( name , reason ) do
321+ case reason do
322+ # NOTE:
323+ # ** (Mix.Error) httpc request failed with: ... Could not install Hex because Mix could not download metadata ...
324+ { :shutdown , { :error , :normal , message } } ->
325+ "Engine #{ name } shutdown with error:\n \n #{ message } "
326+
327+ { :shutdown , { :node_exit , node_exit } } ->
328+ "Engine #{ name } exit with status #{ node_exit . status } , last message:\n \n #{ node_exit . last_message } "
329+
330+ reason ->
331+ "Failed to start engine #{ name } : #{ inspect ( reason ) } "
332+ end
333+ end
334+
335+ defp log_prepend_project_root ( message , % State {
336+ configuration: % Expert.Configuration { project: % Forge.Project { } = project }
337+ } ) do
338+ "[Project #{ project . root_uri } ] #{ message } "
339+ end
340+
341+ defp log_prepend_project_root ( message , _state ) , do: message
276342end
0 commit comments