11defmodule Expert do
2+ alias Expert.ActiveProjects
23 alias Expert.Project
34 alias Expert.Protocol.Convert
45 alias Expert.Protocol.Id
56 alias Expert.Provider.Handlers
67 alias Expert.State
8+ alias Forge.Project
79 alias GenLSP.Enumerations
810 alias GenLSP.Requests
911 alias GenLSP.Structures
@@ -16,6 +18,7 @@ defmodule Expert do
1618 GenLSP.Notifications.TextDocumentDidChange ,
1719 GenLSP.Notifications.WorkspaceDidChangeConfiguration ,
1820 GenLSP.Notifications.WorkspaceDidChangeWatchedFiles ,
21+ GenLSP.Notifications.WorkspaceDidChangeWorkspaceFolders ,
1922 GenLSP.Notifications.TextDocumentDidClose ,
2023 GenLSP.Notifications.TextDocumentDidOpen ,
2124 GenLSP.Notifications.TextDocumentDidSave ,
@@ -54,17 +57,28 @@ defmodule Expert do
5457
5558 with { :ok , response , state } <- State . initialize ( state , request ) ,
5659 { :ok , response } <- Expert.Protocol.Convert . to_lsp ( response ) do
57- Task.Supervisor . start_child ( :expert_task_queue , fn ->
58- # dirty sleep to allow initialize response to return before progress reports
59- Process . sleep ( 50 )
60- config = state . configuration
60+ workspace_folders = request . params . workspace_folders || [ ]
6161
62- log_info ( lsp , "Starting project" )
62+ projects =
63+ for % { uri: uri } <- workspace_folders ,
64+ project = Project . new ( uri ) ,
65+ # Only include Mix projects, or include single-folder workspaces with
66+ # bare elixir files.
67+ project . mix_project? || Project . elixir_project? ( project ) do
68+ project
69+ end
6370
64- start_result = Project.Supervisor . start ( config . project )
71+ ActiveProjects . set_projects ( projects )
6572
66- send ( Expert , { :engine_initialized , start_result } )
67- end )
73+ for project <- projects do
74+ Task.Supervisor . start_child ( :expert_task_queue , fn ->
75+ log_info ( lsp , project , "Starting project" )
76+
77+ start_result = Expert.Project.Supervisor . ensure_node_started ( project )
78+
79+ send ( Expert , { :engine_initialized , project , start_result } )
80+ end )
81+ end
6882
6983 { :reply , response , assign ( lsp , state: state ) }
7084 else
@@ -109,43 +123,74 @@ defmodule Expert do
109123 def handle_request ( request , lsp ) do
110124 state = assigns ( lsp ) . state
111125
112- if state . engine_initialized? do
113- with { :ok , handler } <- fetch_handler ( request ) ,
114- { :ok , request } <- Convert . to_native ( request ) ,
115- { :ok , response } <- handler . handle ( request , state . configuration ) ,
116- { :ok , response } <- Expert.Protocol.Convert . to_lsp ( response ) do
117- { :reply , response , lsp }
118- else
119- { :error , { :unhandled , _ } } ->
120- Logger . info ( "Unhandled request: #{ request . method } " )
121-
122- { :reply ,
123- % GenLSP.ErrorResponse {
124- code: GenLSP.Enumerations.ErrorCodes . method_not_found ( ) ,
125- message: "Method not found"
126- } , lsp }
127-
128- error ->
129- message = "Failed to handle #{ request . method } , #{ inspect ( error ) } "
130- Logger . error ( message )
131-
132- { :reply ,
133- % GenLSP.ErrorResponse {
134- code: GenLSP.Enumerations.ErrorCodes . internal_error ( ) ,
135- message: message
136- } , lsp }
137- end
126+ with { :ok , handler } <- fetch_handler ( request ) ,
127+ { :ok , request } <- Convert . to_native ( request ) ,
128+ :ok <- check_engine_initialized ( request ) ,
129+ { :ok , response } <- handler . handle ( request , state . configuration ) ,
130+ { :ok , response } <- Expert.Protocol.Convert . to_lsp ( response ) do
131+ { :reply , response , lsp }
138132 else
139- GenLSP . warning (
140- lsp ,
141- "Received request #{ request . method } before engine was initialized. Ignoring."
142- )
133+ { :error , { :unhandled , _ } } ->
134+ Logger . info ( "Unhandled request: #{ request . method } " )
143135
144- { :noreply , lsp }
136+ { :reply ,
137+ % GenLSP.ErrorResponse {
138+ code: GenLSP.Enumerations.ErrorCodes . method_not_found ( ) ,
139+ message: "Method not found"
140+ } , lsp }
141+
142+ { :error , :engine_not_initialized , project } ->
143+ GenLSP . info (
144+ lsp ,
145+ "Received request #{ request . method } before engine for #{ project && Project . name ( project ) } was initialized. Ignoring."
146+ )
147+
148+ { :reply , nil , lsp }
149+
150+ error ->
151+ message = "Failed to handle #{ request . method } , #{ inspect ( error ) } "
152+ Logger . error ( message )
153+
154+ { :reply ,
155+ % GenLSP.ErrorResponse {
156+ code: GenLSP.Enumerations.ErrorCodes . internal_error ( ) ,
157+ message: message
158+ } , lsp }
145159 end
146160 end
147161
162+ defp check_engine_initialized ( request ) do
163+ if document_request? ( request ) do
164+ case Forge.Document.Container . context_document ( request , nil ) do
165+ % Forge.Document { } = document ->
166+ projects = ActiveProjects . projects ( )
167+ project = Project . project_for_document ( projects , document )
168+
169+ if ActiveProjects . active? ( project ) do
170+ :ok
171+ else
172+ { :error , :engine_not_initialized , project }
173+ end
174+
175+ nil ->
176+ { :error , :engine_not_initialized , nil }
177+ end
178+ else
179+ :ok
180+ end
181+ end
182+
183+ defp document_request? ( % { document: % Forge.Document { } } ) , do: true
184+
185+ defp document_request? ( % { params: params } ) do
186+ document_request? ( params )
187+ end
188+
189+ defp document_request? ( % { text_document: % { uri: _ } } ) , do: true
190+ defp document_request? ( _ ) , do: false
191+
148192 def handle_notification ( % GenLSP.Notifications.Initialized { } , lsp ) do
193+ Logger . info ( "Server initialized, registering capabilities" )
149194 registrations = registrations ( )
150195
151196 if nil != GenLSP . request ( lsp , registrations ) do
@@ -189,35 +234,33 @@ defmodule Expert do
189234 end
190235 end
191236
192- def handle_info ( { :engine_initialized , { :ok , _pid } } , lsp ) do
193- state = assigns ( lsp ) . state
194-
195- new_state = % { state | engine_initialized?: true }
196-
197- lsp = assign ( lsp , state: new_state )
198-
199- Logger . info ( "Engine initialized" )
237+ def handle_info ( { :engine_initialized , project , { :ok , _pid } } , lsp ) do
238+ log_info (
239+ lsp ,
240+ project ,
241+ "Engine initialized for project #{ Project . name ( project ) } "
242+ )
200243
201244 { :noreply , lsp }
202245 end
203246
204- def handle_info ( { :engine_initialized , { :error , reason } } , lsp ) do
247+ def handle_info ( { :engine_initialized , project , { :error , reason } } , lsp ) do
205248 error_message = initialization_error_message ( reason )
206- log_error ( lsp , error_message )
249+ log_error ( lsp , project , error_message )
207250
208251 { :noreply , lsp }
209252 end
210253
211- def log_info ( lsp \\ get_lsp ( ) , message ) do
212- message = log_prepend_project_root ( message , assigns ( lsp ) . state )
254+ def log_info ( lsp \\ get_lsp ( ) , project , message ) do
255+ message = log_prepend_project_root ( message , project )
213256
214257 Logger . info ( message )
215258 GenLSP . info ( lsp , message )
216259 end
217260
218261 # 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 )
262+ def log_error ( lsp \\ get_lsp ( ) , project , message ) do
263+ message = log_prepend_project_root ( message , project )
221264
222265 Logger . error ( message )
223266 GenLSP . error ( lsp , message )
@@ -335,11 +378,7 @@ defmodule Expert do
335378 end
336379 end
337380
338- defp log_prepend_project_root ( message , % State {
339- configuration: % Expert.Configuration { project: % Forge.Project { } = project }
340- } ) do
381+ defp log_prepend_project_root ( message , project ) do
341382 "[Project #{ project . root_uri } ] #{ message } "
342383 end
343-
344- defp log_prepend_project_root ( message , _state ) , do: message
345384end
0 commit comments