@@ -632,73 +632,135 @@ export namespace LSPServer {
632632 root : NearestRoot ( [ "compile_commands.json" , "compile_flags.txt" , ".clangd" , "CMakeLists.txt" , "Makefile" ] ) ,
633633 extensions : [ ".c" , ".cpp" , ".cc" , ".cxx" , ".c++" , ".h" , ".hpp" , ".hh" , ".hxx" , ".h++" ] ,
634634 async spawn ( root ) {
635- let bin = Bun . which ( "clangd" , {
636- PATH : process . env [ "PATH" ] + ":" + Global . Path . bin ,
637- } )
638- if ( ! bin ) {
639- if ( Flag . OPENCODE_DISABLE_LSP_DOWNLOAD ) return
640- log . info ( "downloading clangd from GitHub releases" )
635+ const args = [ "--background-index" , "--clang-tidy" ]
636+ const fromPath = Bun . which ( "clangd" )
637+ if ( fromPath ) {
638+ return {
639+ process : spawn ( fromPath , args , {
640+ cwd : root ,
641+ } ) ,
642+ }
643+ }
641644
642- const releaseResponse = await fetch ( "https://api.github.com/repos/clangd/clangd/releases/latest" )
643- if ( ! releaseResponse . ok ) {
644- log . error ( "Failed to fetch clangd release info" )
645- return
645+ const ext = process . platform === "win32" ? ".exe" : ""
646+ const direct = path . join ( Global . Path . bin , "clangd" + ext )
647+ if ( await Bun . file ( direct ) . exists ( ) ) {
648+ return {
649+ process : spawn ( direct , args , {
650+ cwd : root ,
651+ } ) ,
646652 }
653+ }
647654
648- const release = ( await releaseResponse . json ( ) ) as any
655+ const entries = await fs . readdir ( Global . Path . bin , { withFileTypes : true } ) . catch ( ( ) => [ ] )
656+ for ( const entry of entries ) {
657+ if ( ! entry . isDirectory ( ) ) continue
658+ if ( ! entry . name . startsWith ( "clangd_" ) ) continue
659+ const candidate = path . join ( Global . Path . bin , entry . name , "bin" , "clangd" + ext )
660+ if ( await Bun . file ( candidate ) . exists ( ) ) {
661+ return {
662+ process : spawn ( candidate , args , {
663+ cwd : root ,
664+ } ) ,
665+ }
666+ }
667+ }
649668
650- const platform = process . platform
651- let assetName = ""
669+ if ( Flag . OPENCODE_DISABLE_LSP_DOWNLOAD ) return
670+ log . info ( "downloading clangd from GitHub releases" )
652671
653- if ( platform === "darwin" ) {
654- assetName = "clangd-mac-"
655- } else if ( platform === "linux" ) {
656- assetName = "clangd-linux-"
657- } else if ( platform === "win32" ) {
658- assetName = "clangd-windows-"
659- } else {
660- log . error ( `Platform ${ platform } is not supported by clangd auto-download` )
661- return
662- }
672+ const releaseResponse = await fetch ( "https://api.github.com/repos/clangd/clangd/releases/latest" )
673+ if ( ! releaseResponse . ok ) {
674+ log . error ( "Failed to fetch clangd release info" )
675+ return
676+ }
663677
664- assetName += release . tag_name + ".zip"
678+ const release : {
679+ tag_name ?: string
680+ assets ?: { name ?: string ; browser_download_url ?: string } [ ]
681+ } = await releaseResponse . json ( )
665682
666- const asset = release . assets . find ( ( a : any ) => a . name === assetName )
667- if ( ! asset ) {
668- log . error ( `Could not find asset ${ assetName } in latest clangd release` )
669- return
670- }
683+ const tag = release . tag_name
684+ if ( ! tag ) {
685+ log . error ( "clangd release did not include a tag name" )
686+ return
687+ }
688+ const platform = process . platform
689+ const tokens : Record < string , string > = {
690+ darwin : "mac" ,
691+ linux : "linux" ,
692+ win32 : "windows" ,
693+ }
694+ const token = tokens [ platform ]
695+ if ( ! token ) {
696+ log . error ( `Platform ${ platform } is not supported by clangd auto-download` )
697+ return
698+ }
671699
672- const downloadUrl = asset . browser_download_url
673- const downloadResponse = await fetch ( downloadUrl )
674- if ( ! downloadResponse . ok ) {
675- log . error ( "Failed to download clangd" )
676- return
677- }
700+ const assets = release . assets ?? [ ]
701+ const valid = ( item : { name ?: string ; browser_download_url ?: string } ) => {
702+ if ( ! item . name ) return false
703+ if ( ! item . browser_download_url ) return false
704+ if ( ! item . name . includes ( token ) ) return false
705+ return item . name . includes ( tag )
706+ }
678707
679- const zipPath = path . join ( Global . Path . bin , "clangd.zip" )
680- await Bun . file ( zipPath ) . write ( downloadResponse )
708+ const asset =
709+ assets . find ( ( item ) => valid ( item ) && item . name ?. endsWith ( ".zip" ) ) ??
710+ assets . find ( ( item ) => valid ( item ) && item . name ?. endsWith ( ".tar.xz" ) ) ??
711+ assets . find ( ( item ) => valid ( item ) )
712+ if ( ! asset ?. name || ! asset . browser_download_url ) {
713+ log . error ( "clangd could not match release asset" , { tag, platform } )
714+ return
715+ }
681716
682- await $ `unzip -o -q ${ zipPath } ` . quiet ( ) . cwd ( Global . Path . bin ) . nothrow ( )
683- await fs . rm ( zipPath , { force : true } )
717+ const name = asset . name
718+ const downloadResponse = await fetch ( asset . browser_download_url )
719+ if ( ! downloadResponse . ok ) {
720+ log . error ( "Failed to download clangd" )
721+ return
722+ }
723+
724+ const archive = path . join ( Global . Path . bin , name )
725+ const buf = await downloadResponse . arrayBuffer ( )
726+ if ( buf . byteLength === 0 ) {
727+ log . error ( "Failed to write clangd archive" )
728+ return
729+ }
730+ await Bun . write ( archive , buf )
684731
685- const extractedDir = path . join ( Global . Path . bin , assetName . replace ( ".zip" , "" ) )
686- bin = path . join ( extractedDir , "bin" , "clangd" + ( platform === "win32" ? ".exe" : "" ) )
732+ const zip = name . endsWith ( ".zip" )
733+ const tar = name . endsWith ( ".tar.xz" )
734+ if ( ! zip && ! tar ) {
735+ log . error ( "clangd encountered unsupported asset" , { asset : name } )
736+ return
737+ }
687738
688- if ( ! ( await Bun . file ( bin ) . exists ( ) ) ) {
689- log . error ( "Failed to extract clangd binary" )
690- return
691- }
739+ if ( zip ) {
740+ await $ `unzip -o -q ${ archive } ` . quiet ( ) . cwd ( Global . Path . bin ) . nothrow ( )
741+ }
742+ if ( tar ) {
743+ await $ `tar -xf ${ archive } ` . cwd ( Global . Path . bin ) . nothrow ( )
744+ }
745+ await fs . rm ( archive , { force : true } )
692746
693- if ( platform !== "win32" ) {
694- await $ `chmod +x ${ bin } ` . nothrow ( )
695- }
747+ const bin = path . join ( Global . Path . bin , "clangd_" + tag , "bin" , "clangd" + ext )
748+ if ( ! ( await Bun . file ( bin ) . exists ( ) ) ) {
749+ log . error ( "Failed to extract clangd binary" )
750+ return
751+ }
696752
697- log . info ( `installed clangd` , { bin } )
753+ if ( platform !== "win32" ) {
754+ await $ `chmod +x ${ bin } ` . nothrow ( )
698755 }
699756
757+ await fs . unlink ( path . join ( Global . Path . bin , "clangd" ) ) . catch ( ( ) => { } )
758+ await fs . symlink ( bin , path . join ( Global . Path . bin , "clangd" ) ) . catch ( ( ) => { } )
759+
760+ log . info ( `installed clangd` , { bin } )
761+
700762 return {
701- process : spawn ( bin , [ "--background-index" , "--clang-tidy" ] , {
763+ process : spawn ( bin , args , {
702764 cwd : root ,
703765 } ) ,
704766 }
0 commit comments