package Ġorg . jav acs . kt Ċ Ċ import Ġorg . jetbrains . kotlin . com . intellij . openapi . util . text . StringUtil . convert Line Separ ators Ċ import Ġorg . eclipse . lsp 4 j . TextDocument Content ChangeEvent Ċ import Ġorg . jav acs . kt . util . Kotlin LS Exception Ċ import Ġjava . io . BufferedReader Ċ import Ġjava . io . String Reader Ċ import Ġjava . io . String Writer Ċ import Ġjava . io . IOException Ċ import Ġjava . nio . file . FileSystem s Ċ import Ġjava . nio . file . Files Ċ import Ġjava . nio . file . Path Ċ import Ġjava . util . stream . Collectors Ċ Ċ private Ġclass ĠSource Version ( val Ġcontent : ĠString , Ġval Ġversion : ĠInt ) Ċ Ċ /** Ċ Ġ* ĠNotify ĠSource Path Ġwhenever Ġa Ġfile Ġchanges Ċ Ġ*/ Ċ private Ġclass ĠNotify Source Path ( private Ġval Ġsp : ĠSource Path ) Ġ{ ĊĠĠĠ Ġprivate Ġval Ġfiles Ġ= Ġmutable Map Of < Path , ĠSource Version >() ĊĊĠĠĠ Ġoperator Ġfun Ġget ( file : ĠPath ): ĠSource Version ? Ġ= Ġfiles [ file ] ĊĊĠĠĠ Ġoperator Ġfun Ġset ( file : ĠPath , Ġsource : ĠSource Version ) Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġcontent Ġ= Ġconvert Line Separ ators ( source . content ) ĊĊĠĠĠĠĠĠĠ Ġfiles [ file ] Ġ= Ġsource ĊĠĠĠĠĠĠĠ Ġsp . put ( file , Ġcontent ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġfun Ġremove ( file : ĠPath ) Ġ{ ĊĠĠĠĠĠĠĠ Ġfiles . remove ( file ) ĊĠĠĠĠĠĠĠ Ġsp . delete ( file ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġfun Ġremove All ( rm : ĠCollection < Path >) Ġ{ ĊĠĠĠĠĠĠĠ Ġfiles Ġ-= Ġrm ĊĊĠĠĠĠĠĠĠ Ġrm . forEach ( sp :: delete ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġval Ġkeys Ġget () Ġ= Ġfiles . keys Ċ } Ċ Ċ /** Ċ Ġ* ĠKeep Ġtrack Ġof Ġthe Ġtext Ġof Ġall Ġfiles Ġin Ġthe Ġworkspace Ċ Ġ*/ Ċ class ĠSource Files ( private Ġval Ġsp : ĠSource Path ) Ġ{ ĊĠĠĠ Ġprivate Ġval Ġworkspace Roots Ġ= Ġmutable Set Of < Path >() ĊĠĠĠ Ġprivate Ġvar Ġex clusions Ġ= ĠSource Ex clusions ( workspace Roots ) ĊĠĠĠ Ġprivate Ġval Ġfiles Ġ= ĠNotify Source Path ( sp ) ĊĠĠĠ Ġprivate Ġval Ġopen Ġ= Ġmutable Set Of < Path >() ĊĊĠĠĠ Ġfun Ġopen ( file : ĠPath , Ġcontent : ĠString , Ġversion : ĠInt ) Ġ{ ĊĠĠĠĠĠĠĠ Ġif Ġ( ex clusions . is Included ( file )) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġfiles [ file ] Ġ= ĠSource Version ( content , Ġversion ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġopen . add ( file ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġfun Ġclose ( file : ĠPath ) Ġ{ ĊĠĠĠĠĠĠĠ Ġif Ġ( ex clusions . is Included ( file )) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġopen . remove ( file ) ĊĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġdisk Ġ= Ġread From Disk ( file ) ĊĊĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ( disk Ġ!= Ġnull ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġfiles [ file ] Ġ= Ġdisk ĊĠĠĠĠĠĠĠĠĠĠĠ Ġelse ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġfiles . remove ( file ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġfun Ġedit ( file : ĠPath , Ġnew Version : ĠInt , Ġcontent Changes : ĠList < TextDocument Content ChangeEvent >) Ġ{ ĊĠĠĠĠĠĠĠ Ġif Ġ( ex clusions . is Included ( file )) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġexisting Ġ= Ġfiles [ file ] !! ĊĠĠĠĠĠĠĠĠĠĠĠ Ġvar Ġnew Text Ġ= Ġexisting . content ĊĊĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ( new Version Ġ<= Ġexisting . version ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠLOG . warn (" Ignored Ġ{} Ġversion Ġ{}", Ġfile . fileName , Ġnew Version ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġreturn ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠĠĠĠĠ Ġfor Ġ( change Ġin Ġcontent Changes ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ( change . range Ġ== Ġnull ) Ġnew Text Ġ= Ġchange . text ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġelse Ġnew Text Ġ= Ġpatch ( new Text , Ġchange ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠĠĠĠĠ Ġfiles [ file ] Ġ= ĠSource Version ( new Text , Ġnew Version ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġfun Ġcreated On Disk ( file : ĠPath ) Ġ{ ĊĠĠĠĠĠĠĠ Ġchanged On Disk ( file ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġfun Ġdeleted On Disk ( file : ĠPath ) Ġ{ ĊĠĠĠĠĠĠĠ Ġif Ġ( is Source ( file )) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġfiles . remove ( file ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġfun Ġchanged On Disk ( file : ĠPath ) Ġ{ ĊĠĠĠĠĠĠĠ Ġif Ġ( is Source ( file )) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġfiles [ file ] Ġ= Ġread From Disk ( file ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ?: Ġthrow ĠKotlin LS Exception (" Could Ġnot Ġread Ġsource Ġfile Ġ'$ file ' Ġafter Ġbeing Ġchanged Ġon Ġdisk ") ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġread From Disk ( file : ĠPath ): ĠSource Version ? Ġ{ ĊĠĠĠĠĠĠĠ Ġif Ġ(! Files . exists ( file )) Ġreturn Ġnull ĊĊĠĠĠĠĠĠĠ Ġvar Ġcontent Ġ= Ġ"" ĊĊĠĠĠĠĠĠĠ Ġtry Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġcontent Ġ= ĠFiles . read All Lines ( file ). join ToString ("\ n ") ĊĠĠĠĠĠĠĠ Ġ} Ġcatch ( exception : ĠIOException ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ ĠLOG . warn (" Exception Ġwhile Ġparsing Ġsource Ġfile : Ġ{}", Ġfile . to File (). absolute Path ) ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġreturn ĠSource Version ( content , Ġ- 1 ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġis Source ( file : ĠPath ): ĠBoolean Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġname Ġ= Ġfile . fileName . toString () ĊĠĠĠĠĠĠĠ Ġreturn Ġ( name . endsWith (". kt ") Ġ|| Ġname . endsWith (". k ts ")) Ġ&& Ġex clusions . is Included ( file ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġfun Ġadd Workspace Root ( root : ĠPath ) Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġadd Sources Ġ= Ġfind Source Files ( root ) ĊĊĠĠĠĠĠĠĠ Ġlog Added ( add Sources , Ġroot ) ĊĊĠĠĠĠĠĠĠ Ġfor Ġ( file Ġin Ġadd Sources ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġread From Disk ( file )?. let Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġfiles [ file ] Ġ= Ġit ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} Ġ?: ĠLOG . warn (" Could Ġnot Ġread Ġsource Ġfile Ġ'{} '", Ġfile ) ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġworkspace Roots . add ( root ) ĊĠĠĠĠĠĠĠ Ġupdate Ex clusions () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġfun Ġremove Workspace Root ( root : ĠPath ) Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġrm Sources Ġ= Ġfiles . keys . filter Ġ{ Ġit . startsWith ( root ) Ġ} ĊĊĠĠĠĠĠĠĠ Ġlog Removed ( rm Sources , Ġroot ) ĊĊĠĠĠĠĠĠĠ Ġfiles . removeAll ( rm Sources ) ĊĠĠĠĠĠĠĠ Ġworkspace Roots . remove ( root ) ĊĠĠĠĠĠĠĠ Ġupdate Ex clusions () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġupdate Ex clusions () Ġ{ ĊĠĠĠĠĠĠĠ Ġex clusions Ġ= ĠSource Ex clusions ( workspace Roots ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġfun ĠisOpen ( file : ĠPath ): ĠBoolean Ġ= Ġopen . contains ( file ) Ċ } Ċ Ċ private Ġfun Ġpatch ( source Text : ĠString , Ġchange : ĠText Document Content ChangeEvent ): ĠString Ġ{ ĊĠĠĠ Ġval Ġrange Ġ= Ġchange . range ĊĠĠĠ Ġval Ġreader Ġ= ĠBufferedReader ( String Reader ( source Text )) ĊĠĠĠ Ġval Ġwriter Ġ= ĠString Writer () ĊĊĠĠĠ Ġ// ĠSkip Ġunchanged Ġlines ĊĠĠĠ Ġvar Ġline Ġ= Ġ 0 ĊĊĠĠĠ Ġwhile Ġ( line Ġ< Ġrange . start . line ) Ġ{ ĊĠĠĠĠĠĠĠ Ġwriter . write ( reader . readLine () Ġ+ Ġ'\ n ') ĊĠĠĠĠĠĠĠ Ġline ++ ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ// ĠSkip Ġunchanged Ġchars ĊĠĠĠ Ġfor Ġ( character Ġin Ġ 0 Ġuntil Ġrange . start . character ) ĊĠĠĠĠĠĠĠ Ġwriter . write ( reader . read ()) ĊĊĠĠĠ Ġ// ĠWrite Ġreplacement Ġtext ĊĠĠĠ Ġwriter . write ( change . text ) ĊĊĠĠĠ Ġ// ĠSkip Ġreplaced Ġtext ĊĠĠĠ Ġreader . skip ( change . range Length !!. to Long ()) ĊĊĠĠĠ Ġ// ĠWrite Ġremaining Ġtext ĊĠĠĠ Ġwhile Ġ( true ) Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġnext Ġ= Ġreader . read () ĊĊĠĠĠĠĠĠĠ Ġif Ġ( next Ġ== Ġ- 1 ) Ġreturn Ġwriter . toString () ĊĠĠĠĠĠĠĠ Ġelse Ġwriter . write ( next ) ĊĠĠĠ Ġ} Ċ } Ċ Ċ private Ġfun Ġfind Source Files ( root : ĠPath ): ĠSet < Path > Ġ{ ĊĠĠĠ Ġval Ġpattern Ġ= ĠFileSystem s . getDefault (). getPath Matcher (" glob : *. { kt , k ts }") ĊĠĠĠ Ġval Ġex clusions Ġ= ĠSource Ex clusions ( root ) ĊĠĠĠ Ġreturn ĠFiles . walk ( root ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ. filter Ġ{ Ġpattern . matches ( it . fileName ) Ġ&& Ġex clusions . is Included ( it ) Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ. collect ( Collectors . toSet ()) Ċ } Ċ Ċ private Ġfun Ġlog Added ( sources : ĠCollection < Path >, Ġroot Path : ĠPath ?) Ġ{ ĊĠĠĠ ĠLOG . info (" Adding Ġ{} Ġunder Ġ{} Ġto Ġsource Ġpath ", Ġdescribe Files ( sources ), Ġroot Path ) Ċ } Ċ Ċ private Ġfun Ġlog Removed ( sources : ĠCollection < Path >, Ġroot Path : ĠPath ?) Ġ{ ĊĠĠĠ ĠLOG . info (" Removing Ġ{} Ġunder Ġ{} Ġto Ġsource Ġpath ", Ġdescribe Files ( sources ), Ġroot Path ) Ċ } Ċ Ċ fun Ġdescribe Files ( files : ĠCollection < Path >): ĠString Ġ{ ĊĠĠĠ Ġreturn Ġif Ġ( files . isEmpty ()) Ġ" 0 Ġfiles " ĊĠĠĠ Ġelse Ġif Ġ( files . size Ġ> Ġ 5 ) Ġ"${ files . size } Ġfiles " ĊĠĠĠ Ġelse Ġfiles . map Ġ{ Ġ"... /" Ġ+ Ġit . parent . fileName Ġ+ Ġ"/" Ġ+ Ġit . fileName Ġ}. join ToString (", Ġ") Ċ } Ċ