@@ -13,6 +13,8 @@ class LanguageServerApiManager {
1313 private extensionApi : any ;
1414
1515 private isServerReady : boolean = false ;
16+ private isServerRunning : boolean = false ;
17+ private serverReadyWaitStarted : boolean = false ;
1618
1719 public async ready ( ) : Promise < boolean > {
1820 if ( this . isServerReady ) {
@@ -28,11 +30,49 @@ class LanguageServerApiManager {
2830 return false ;
2931 }
3032
33+ // Use serverRunning() if available (API >= 0.14) for progressive loading.
34+ // This resolves when the server process is alive and can handle requests,
35+ // even if project imports haven't completed yet. This enables the tree view
36+ // to show projects incrementally as they are imported.
37+ if ( ! this . isServerRunning && this . extensionApi . serverRunning ) {
38+ await this . extensionApi . serverRunning ( ) ;
39+ this . isServerRunning = true ;
40+ return true ;
41+ }
42+ if ( this . isServerRunning ) {
43+ return true ;
44+ }
45+
46+ // Fallback for older API versions: wait for full server readiness
3147 await this . extensionApi . serverReady ( ) ;
3248 this . isServerReady = true ;
3349 return true ;
3450 }
3551
52+ /**
53+ * Start a background wait for full server readiness (import complete).
54+ * When the server finishes importing, trigger a full refresh to replace
55+ * progressive placeholder items with proper data from the server.
56+ * Guarded so it only starts once regardless of call order.
57+ */
58+ private startServerReadyWait ( ) : void {
59+ if ( this . serverReadyWaitStarted || this . isServerReady ) {
60+ return ;
61+ }
62+ if ( this . extensionApi ?. serverReady ) {
63+ this . serverReadyWaitStarted = true ;
64+ this . extensionApi . serverReady ( )
65+ . then ( ( ) => {
66+ this . isServerReady = true ;
67+ commands . executeCommand ( Commands . VIEW_PACKAGE_INTERNAL_REFRESH , /* debounce = */ false ) ;
68+ } )
69+ . catch ( ( _error : unknown ) => {
70+ // Server failed to become ready (e.g., startup failure).
71+ // Leave isServerReady as false; progressive items remain as-is.
72+ } ) ;
73+ }
74+ }
75+
3676 public async initializeJavaLanguageServerApis ( ) : Promise < void > {
3777 if ( this . isApiInitialized ( ) ) {
3878 return ;
@@ -49,18 +89,39 @@ class LanguageServerApiManager {
4989 }
5090
5191 this . extensionApi = extensionApi ;
92+ // Start background wait for full server readiness unconditionally.
93+ // This ensures isServerReady is set and final refresh fires even
94+ // if onDidProjectsImport sets isServerRunning before ready() runs.
95+ this . startServerReadyWait ( ) ;
96+
5297 if ( extensionApi . onDidClasspathUpdate ) {
5398 const onDidClasspathUpdate : Event < Uri > = extensionApi . onDidClasspathUpdate ;
54- contextManager . context . subscriptions . push ( onDidClasspathUpdate ( ( ) => {
55- commands . executeCommand ( Commands . VIEW_PACKAGE_INTERNAL_REFRESH , /* debounce = */ true ) ;
99+ contextManager . context . subscriptions . push ( onDidClasspathUpdate ( ( uri : Uri ) => {
100+ if ( this . isServerReady ) {
101+ // Server is fully ready — do a normal refresh to get full project data.
102+ commands . executeCommand ( Commands . VIEW_PACKAGE_INTERNAL_REFRESH , /* debounce = */ true ) ;
103+ } else {
104+ // During import, the server is blocked and can't respond to queries.
105+ // Don't clear progressive items. Try to add the project if not
106+ // already present (typically a no-op since ProjectsImported fires first).
107+ commands . executeCommand ( Commands . VIEW_PACKAGE_INTERNAL_ADD_PROJECTS , [ uri . toString ( ) ] ) ;
108+ }
56109 syncHandler . updateFileWatcher ( Settings . autoRefresh ( ) ) ;
57110 } ) ) ;
58111 }
59112
60113 if ( extensionApi . onDidProjectsImport ) {
61114 const onDidProjectsImport : Event < Uri [ ] > = extensionApi . onDidProjectsImport ;
62- contextManager . context . subscriptions . push ( onDidProjectsImport ( ( ) => {
63- commands . executeCommand ( Commands . VIEW_PACKAGE_INTERNAL_REFRESH , /* debounce = */ true ) ;
115+ contextManager . context . subscriptions . push ( onDidProjectsImport ( ( uris : Uri [ ] ) => {
116+ // Server is sending project data, so it's definitely running.
117+ // Mark as running so ready() returns immediately on subsequent calls.
118+ this . isServerRunning = true ;
119+ // During import, the JDTLS server is blocked by Eclipse workspace
120+ // operations and cannot respond to queries. Instead of triggering
121+ // a refresh (which queries the server), directly add projects to
122+ // the tree view from the notification data.
123+ const projectUris = uris . map ( u => u . toString ( ) ) ;
124+ commands . executeCommand ( Commands . VIEW_PACKAGE_INTERNAL_ADD_PROJECTS , projectUris ) ;
64125 syncHandler . updateFileWatcher ( Settings . autoRefresh ( ) ) ;
65126 } ) ) ;
66127 }
@@ -91,6 +152,14 @@ class LanguageServerApiManager {
91152 return this . extensionApi !== undefined ;
92153 }
93154
155+ /**
156+ * Returns true if the server has fully completed initialization (import finished).
157+ * During progressive loading, this returns false even though ready() has resolved.
158+ */
159+ public isFullyReady ( ) : boolean {
160+ return this . isServerReady ;
161+ }
162+
94163 /**
95164 * Check if the language server is ready in the given timeout.
96165 * @param timeout the timeout in milliseconds to wait
0 commit comments