@@ -4,9 +4,11 @@ import LanguageClient
44import LanguageServerProtocol
55import Logger
66import Preferences
7+ import XcodeInspector
78
89protocol CodeiumLSP {
910 func sendRequest< E: CodeiumRequestType > ( _ endpoint: E ) async throws -> E . Response
11+ func updateIndexing( ) async
1012 func terminate( )
1113}
1214
@@ -20,6 +22,7 @@ final class CodeiumLanguageServer {
2022 var launchHandler : ( ( ) -> Void ) ?
2123 var port : String ?
2224 var heartbeatTask : Task < Void , Error > ?
25+ var project_paths : [ String ]
2326
2427 init (
2528 languageServerExecutableURL: URL ,
@@ -33,6 +36,7 @@ final class CodeiumLanguageServer {
3336 self . supportURL = supportURL
3437 self . terminationHandler = terminationHandler
3538 self . launchHandler = launchHandler
39+ self . project_paths = [ ]
3640 process = Process ( )
3741 transport = IOTransport ( )
3842
@@ -60,6 +64,18 @@ final class CodeiumLanguageServer {
6064 if isEnterpriseMode {
6165 process. arguments? . append ( " --enterprise_mode " )
6266 }
67+
68+ let indexEnabled = UserDefaults . shared. value ( for: \. indexEnabled)
69+ if indexEnabled {
70+ let indexingMaxFileSize = UserDefaults . shared. value ( for: \. indexingMaxFileSize)
71+ if ( indexEnabled) {
72+ process. arguments? . append ( " --enable_local_search " )
73+ process. arguments? . append ( " --enable_index_service " )
74+ process. arguments? . append ( " --search_max_workspace_file_count " )
75+ process. arguments? . append ( " \( indexingMaxFileSize) " )
76+ Logger . codeium. info ( " Indexing Enabled " )
77+ }
78+ }
6379
6480 process. currentDirectoryURL = supportURL
6581
@@ -177,6 +193,38 @@ extension CodeiumLanguageServer: CodeiumLSP {
177193 }
178194 }
179195 }
196+
197+ func updateIndexing( ) async {
198+ let indexEnabled = UserDefaults . shared. value ( for: \. indexEnabled)
199+ if !indexEnabled {
200+ return
201+ }
202+
203+
204+ let curr_proj_paths = await getProjectPaths ( )
205+
206+ // Add all workspaces that are in the curr_proj_paths but not in the previous project paths
207+ for curr_proj_path in curr_proj_paths {
208+ if !self . project_paths. contains ( curr_proj_path) && FileManager . default. fileExists ( atPath: curr_proj_path) {
209+ _ = try ? await self . sendRequest ( CodeiumRequest . AddTrackedWorkspace ( requestBody: . init(
210+ workspace: curr_proj_path
211+ ) ) )
212+ }
213+ }
214+
215+ // Remove all workspaces that are in previous project paths but not in the curr_proj_paths
216+ for proj_path in self . project_paths {
217+ if !curr_proj_paths. contains ( proj_path) && FileManager . default. fileExists ( atPath: proj_path) {
218+ _ = try ? await self . sendRequest ( CodeiumRequest . RemoveTrackedWorkspace ( requestBody: . init(
219+ workspace: proj_path
220+ ) ) )
221+ }
222+ }
223+ // These should be identical now
224+ self . project_paths = curr_proj_paths
225+
226+ }
227+
180228}
181229
182230final class IOTransport {
@@ -272,3 +320,70 @@ final class IOTransport {
272320 }
273321}
274322
323+ class WorkspaceParser : NSObject , XMLParserDelegate {
324+ var projectPaths : [ String ] = [ ]
325+ var workspaceFileURL : URL
326+ var workspaceBaseURL : URL
327+
328+ init ( workspaceFileURL: URL , workspaceBaseURL: URL ) {
329+ self . workspaceFileURL = workspaceFileURL
330+ self . workspaceBaseURL = workspaceBaseURL
331+ }
332+
333+ func parse( ) -> [ String ] {
334+ guard let parser = XMLParser ( contentsOf: workspaceFileURL) else {
335+ print ( " Failed to create XML parser for file: \( workspaceFileURL. path) " )
336+ return [ ]
337+ }
338+ parser. delegate = self
339+ parser. parse ( )
340+ return projectPaths
341+ }
342+
343+ // XMLParserDelegate methods
344+ func parser( _ parser: XMLParser , didStartElement elementName: String , namespaceURI: String ? , qualifiedName qName: String ? , attributes attributeDict: [ String : String ] ) {
345+ if elementName == " FileRef " , let location = attributeDict [ " location " ] {
346+ var project_path : String
347+ if location. starts ( with: " group: " ) && pathEndsWithXcodeproj ( location) {
348+ let curr_path = String ( location. dropFirst ( " group: " . count) )
349+ guard let relative_project_url = URL ( string: curr_path) else {
350+ return
351+ }
352+ let relative_base_path = relative_project_url. deletingLastPathComponent ( )
353+ project_path = ( self . workspaceBaseURL. appendingPathComponent ( relative_base_path. relativePath) ) . standardized. path
354+ } else if location. starts ( with: " absolute: " ) && pathEndsWithXcodeproj ( location) {
355+ let abs_url = URL ( fileURLWithPath: String ( location. dropFirst ( " absolute: " . count) ) )
356+ project_path = abs_url. deletingLastPathComponent ( ) . standardized. path
357+ } else {
358+ return
359+ }
360+ if FileManager . default. fileExists ( atPath: project_path) {
361+
362+ projectPaths. append ( project_path)
363+ }
364+ }
365+ }
366+
367+ func parser( _ parser: XMLParser , parseErrorOccurred parseError: Error ) {
368+ print ( " Failed to parse XML: \( parseError. localizedDescription) " )
369+ }
370+
371+ func pathEndsWithXcodeproj( _ path: String ) -> Bool {
372+ return path. hasSuffix ( " .xcodeproj " )
373+ }
374+
375+ }
376+
377+ public func getProjectPaths( ) async -> [ String ] {
378+ guard let workspaceURL = await XcodeInspector . shared. safe. realtimeActiveWorkspaceURL else {
379+ return [ ]
380+ }
381+
382+ let workspacebaseURL = workspaceURL. deletingLastPathComponent ( )
383+
384+ let workspaceContentsURL = workspaceURL. appendingPathComponent ( " contents.xcworkspacedata " )
385+
386+ let parser = WorkspaceParser ( workspaceFileURL: workspaceContentsURL, workspaceBaseURL: workspacebaseURL)
387+ let absolutePaths = parser. parse ( )
388+ return absolutePaths
389+ }
0 commit comments