33//! This module implements the LSP session abstraction that manages project-specific
44//! state and the Salsa database for incremental computation.
55
6- use std:: sync:: Arc ;
7-
86use camino:: Utf8PathBuf ;
9- use dashmap:: DashMap ;
107use djls_conf:: Settings ;
118use djls_project:: Db as ProjectDb ;
129use djls_project:: Interpreter ;
@@ -16,6 +13,7 @@ use djls_workspace::paths;
1613use djls_workspace:: PositionEncoding ;
1714use djls_workspace:: TextDocument ;
1815use djls_workspace:: Workspace ;
16+ use djls_workspace:: WorkspaceFileEvent ;
1917use salsa:: Setter ;
2018use tower_lsp_server:: lsp_types;
2119use url:: Url ;
@@ -74,9 +72,7 @@ impl Session {
7472 } ;
7573
7674 let workspace = Workspace :: new ( ) ;
77-
78- let files = Arc :: new ( DashMap :: new ( ) ) ;
79- let mut db = DjangoDatabase :: new ( workspace. file_system ( ) , files) ;
75+ let mut db = DjangoDatabase :: new ( workspace. file_system ( ) ) ;
8076
8177 if let Some ( root_path) = & project_path {
8278 db. set_project ( root_path) ;
@@ -160,24 +156,8 @@ impl Session {
160156 /// the database or invalidates it if it already exists.
161157 /// For template files, immediately triggers parsing and validation.
162158 pub fn open_document ( & mut self , url : & Url , document : TextDocument ) {
163- // Add to workspace buffers
164- self . workspace . open_document ( url, document) ;
165-
166- if let Some ( path) = paths:: url_to_path ( url) {
167- let already_exists = self . db . has_file ( & path) ;
168- let file = self . db . get_or_create_file ( & path) ;
169-
170- if already_exists {
171- // File was already read - touch to invalidate cache
172- self . db . touch_file ( & path) ;
173- }
174-
175- if FileKind :: from_path ( & path) == FileKind :: Template {
176- let nodelist = djls_templates:: parse_template ( & self . db , file) ;
177- if let Some ( nodelist) = nodelist {
178- djls_semantic:: validate_nodelist ( & self . db , nodelist) ;
179- }
180- }
159+ if let Some ( event) = self . workspace . open_document ( & mut self . db , url, document) {
160+ self . handle_file_event ( & event) ;
181161 }
182162 }
183163
@@ -191,39 +171,20 @@ impl Session {
191171 changes : Vec < lsp_types:: TextDocumentContentChangeEvent > ,
192172 version : i32 ,
193173 ) {
194- self . workspace
195- . update_document ( url, changes, version, self . position_encoding ) ;
196-
197- if let Some ( path) = paths:: url_to_path ( url) {
198- if self . db . has_file ( & path) {
199- // Touch file in database to trigger invalidation
200- self . db . touch_file ( & path) ;
201-
202- if FileKind :: from_path ( & path) == FileKind :: Template {
203- let file = self . db . get_or_create_file ( & path) ;
204- let nodelist = djls_templates:: parse_template ( & self . db , file) ;
205- if let Some ( nodelist) = nodelist {
206- djls_semantic:: validate_nodelist ( & self . db , nodelist) ;
207- }
208- }
209- }
174+ if let Some ( event) = self . workspace . update_document (
175+ & mut self . db ,
176+ url,
177+ changes,
178+ version,
179+ self . position_encoding ,
180+ ) {
181+ self . handle_file_event ( & event) ;
210182 }
211183 }
212184
213185 pub fn save_document ( & mut self , url : & Url ) {
214- if let Some ( path) = paths:: url_to_path ( url) {
215- if self . db . has_file ( & path) {
216- // Touch file in database to trigger invalidation
217- self . db . touch_file ( & path) ;
218-
219- if FileKind :: from_path ( & path) == FileKind :: Template {
220- let file = self . db . get_or_create_file ( & path) ;
221- let nodelist = djls_templates:: parse_template ( & self . db , file) ;
222- if let Some ( nodelist) = nodelist {
223- djls_semantic:: validate_nodelist ( & self . db , nodelist) ;
224- }
225- }
226- }
186+ if let Some ( event) = self . workspace . save_document ( & mut self . db , url) {
187+ self . handle_file_event ( & event) ;
227188 }
228189 }
229190
@@ -232,16 +193,7 @@ impl Session {
232193 /// Removes from workspace buffers and triggers database invalidation to fall back to disk.
233194 /// For template files, immediately re-parses from disk.
234195 pub fn close_document ( & mut self , url : & Url ) -> Option < TextDocument > {
235- let document = self . workspace . close_document ( url) ;
236-
237- if let Some ( path) = paths:: url_to_path ( url) {
238- if self . db . has_file ( & path) {
239- // Touch file in database to trigger re-read from disk
240- self . db . touch_file ( & path) ;
241- }
242- }
243-
244- document
196+ self . workspace . close_document ( & mut self . db , url)
245197 }
246198
247199 /// Get a document from the buffer if it's open.
@@ -252,7 +204,18 @@ impl Session {
252204
253205 /// Get or create a file in the database.
254206 pub fn get_or_create_file ( & mut self , path : & Utf8PathBuf ) -> File {
255- self . db . get_or_create_file ( path)
207+ self . workspace
208+ . track_file ( & mut self . db , path. as_path ( ) )
209+ . file ( )
210+ }
211+
212+ fn handle_file_event ( & self , event : & WorkspaceFileEvent ) {
213+ if FileKind :: from_path ( event. path ( ) ) == FileKind :: Template {
214+ let nodelist = djls_templates:: parse_template ( & self . db , event. file ( ) ) ;
215+ if let Some ( nodelist) = nodelist {
216+ djls_semantic:: validate_nodelist ( & self . db , nodelist) ;
217+ }
218+ }
256219 }
257220
258221 /// Check if the client supports pull diagnostics.
0 commit comments