1
1
import { ReactElement , useEffect , useRef , useState } from 'react'
2
2
import { createLanguageClientManager , LanguageClientId , StatusChangeEvent as WrapperStatusChangeEvent , LanguageClientManager , WillShutdownParams , Infrastructure , LanguageClientOptions , LanguageClientManagerOptions } from '@codingame/monaco-languageclient-wrapper'
3
+ import { useLocalStorage , writeStorage } from '@rehooks/local-storage'
4
+ import { v4 as uuidv4 } from 'uuid'
3
5
import useIsUserActive from './hooks/useIsUserActive'
4
6
import useShouldShutdownLanguageClient from './hooks/useShouldShutdownLanguageClient'
5
7
import { useLastVersion } from './hooks/useLastVersion'
@@ -21,10 +23,22 @@ export interface LanguageClientProps {
21
23
userInactivityDelay ?: number
22
24
/** Shutdown the language client when the user stay inactive during this duration (default 60 seconds) */
23
25
userInactivityShutdownDelay ?: number
26
+ /** Allow only a single tab to have active language clients (the most recently focused one) */
27
+ singleActiveTab ?: boolean
24
28
}
25
29
26
30
const noop = ( ) => null
27
31
32
+ const ACTIVE_TAB_LOCAL_STORAGE_KEY = 'monaco-lsp-active-tab'
33
+ const currentTab = uuidv4 ( )
34
+ let languageClientCount = 0
35
+
36
+ window . addEventListener ( 'focus' , ( ) => {
37
+ if ( languageClientCount > 0 ) {
38
+ writeStorage ( ACTIVE_TAB_LOCAL_STORAGE_KEY , currentTab )
39
+ }
40
+ } )
41
+
28
42
function LanguageClient ( {
29
43
id,
30
44
infrastructure,
@@ -34,7 +48,8 @@ function LanguageClient ({
34
48
onDidChangeStatus : _onDidChangeStatus ,
35
49
onWillShutdown : _onWillShutdown ,
36
50
userInactivityDelay = 30 * 1000 ,
37
- userInactivityShutdownDelay = 60 * 1000
51
+ userInactivityShutdownDelay = 60 * 1000 ,
52
+ singleActiveTab = true
38
53
} : LanguageClientProps ) : ReactElement | null {
39
54
const onError = useLastVersion ( _onError ?? noop )
40
55
const onDidChangeStatus = useLastVersion ( _onDidChangeStatus ?? noop )
@@ -49,6 +64,9 @@ function LanguageClient ({
49
64
const shouldShutdownLanguageClientForInactivity = useShouldShutdownLanguageClient ( isUserActive , userInactivityShutdownDelay )
50
65
const restartAllowed = ! isUserActive
51
66
67
+ const [ activeTab ] = useLocalStorage ( ACTIVE_TAB_LOCAL_STORAGE_KEY )
68
+ const shouldShutdownLanguageClientAsNotActiveTab = singleActiveTab && activeTab !== currentTab
69
+
52
70
useEffect ( ( ) => {
53
71
if ( willShutdown && restartAllowed ) {
54
72
// eslint-disable-next-line no-console
@@ -59,9 +77,14 @@ function LanguageClient ({
59
77
} , [ willShutdown , restartAllowed ] )
60
78
61
79
useEffect ( ( ) => {
80
+ languageClientCount ++
81
+ if ( window . document . hasFocus ( ) ) {
82
+ writeStorage ( ACTIVE_TAB_LOCAL_STORAGE_KEY , currentTab )
83
+ }
84
+
62
85
setWillShutdown ( false )
63
86
64
- if ( shouldShutdownLanguageClientForInactivity ) {
87
+ if ( shouldShutdownLanguageClientForInactivity || shouldShutdownLanguageClientAsNotActiveTab ) {
65
88
onDidChangeStatus ( {
66
89
status : 'inactivityShutdown'
67
90
} )
@@ -82,6 +105,7 @@ function LanguageClient ({
82
105
} )
83
106
84
107
return ( ) => {
108
+ languageClientCount --
85
109
errorDisposable . dispose ( )
86
110
statusChangeDisposable . dispose ( )
87
111
// eslint-disable-next-line no-console
@@ -94,7 +118,7 @@ function LanguageClient ({
94
118
console . error ( 'Unable to dispose language client' , err )
95
119
} )
96
120
}
97
- } , [ id , counter , shouldShutdownLanguageClientForInactivity , onError , onDidChangeStatus , onWillShutdown , infrastructure , clientOptions , clientManagerOptions ] )
121
+ } , [ id , counter , shouldShutdownLanguageClientForInactivity , onError , onDidChangeStatus , onWillShutdown , infrastructure , clientOptions , clientManagerOptions , shouldShutdownLanguageClientAsNotActiveTab ] )
98
122
99
123
return null
100
124
}
0 commit comments