11import { ReactElement , useEffect , useRef , useState } from 'react'
22import { 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'
35import useIsUserActive from './hooks/useIsUserActive'
46import useShouldShutdownLanguageClient from './hooks/useShouldShutdownLanguageClient'
57import { useLastVersion } from './hooks/useLastVersion'
@@ -21,10 +23,22 @@ export interface LanguageClientProps {
2123 userInactivityDelay ?: number
2224 /** Shutdown the language client when the user stay inactive during this duration (default 60 seconds) */
2325 userInactivityShutdownDelay ?: number
26+ /** Allow only a single tab to have active language clients (the most recently focused one) */
27+ singleActiveTab ?: boolean
2428}
2529
2630const noop = ( ) => null
2731
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+
2842function LanguageClient ( {
2943 id,
3044 infrastructure,
@@ -34,7 +48,8 @@ function LanguageClient ({
3448 onDidChangeStatus : _onDidChangeStatus ,
3549 onWillShutdown : _onWillShutdown ,
3650 userInactivityDelay = 30 * 1000 ,
37- userInactivityShutdownDelay = 60 * 1000
51+ userInactivityShutdownDelay = 60 * 1000 ,
52+ singleActiveTab = true
3853} : LanguageClientProps ) : ReactElement | null {
3954 const onError = useLastVersion ( _onError ?? noop )
4055 const onDidChangeStatus = useLastVersion ( _onDidChangeStatus ?? noop )
@@ -49,6 +64,9 @@ function LanguageClient ({
4964 const shouldShutdownLanguageClientForInactivity = useShouldShutdownLanguageClient ( isUserActive , userInactivityShutdownDelay )
5065 const restartAllowed = ! isUserActive
5166
67+ const [ activeTab ] = useLocalStorage ( ACTIVE_TAB_LOCAL_STORAGE_KEY )
68+ const shouldShutdownLanguageClientAsNotActiveTab = singleActiveTab && activeTab !== currentTab
69+
5270 useEffect ( ( ) => {
5371 if ( willShutdown && restartAllowed ) {
5472 // eslint-disable-next-line no-console
@@ -59,9 +77,14 @@ function LanguageClient ({
5977 } , [ willShutdown , restartAllowed ] )
6078
6179 useEffect ( ( ) => {
80+ languageClientCount ++
81+ if ( window . document . hasFocus ( ) ) {
82+ writeStorage ( ACTIVE_TAB_LOCAL_STORAGE_KEY , currentTab )
83+ }
84+
6285 setWillShutdown ( false )
6386
64- if ( shouldShutdownLanguageClientForInactivity ) {
87+ if ( shouldShutdownLanguageClientForInactivity || shouldShutdownLanguageClientAsNotActiveTab ) {
6588 onDidChangeStatus ( {
6689 status : 'inactivityShutdown'
6790 } )
@@ -82,6 +105,7 @@ function LanguageClient ({
82105 } )
83106
84107 return ( ) => {
108+ languageClientCount --
85109 errorDisposable . dispose ( )
86110 statusChangeDisposable . dispose ( )
87111 // eslint-disable-next-line no-console
@@ -94,7 +118,7 @@ function LanguageClient ({
94118 console . error ( 'Unable to dispose language client' , err )
95119 } )
96120 }
97- } , [ id , counter , shouldShutdownLanguageClientForInactivity , onError , onDidChangeStatus , onWillShutdown , infrastructure , clientOptions , clientManagerOptions ] )
121+ } , [ id , counter , shouldShutdownLanguageClientForInactivity , onError , onDidChangeStatus , onWillShutdown , infrastructure , clientOptions , clientManagerOptions , shouldShutdownLanguageClientAsNotActiveTab ] )
98122
99123 return null
100124}
0 commit comments