Something went wrong. Try again.
source dump of claude code forked from oppi.li/claude-code
Something went wrong. Try again.
5.8 kB · 191 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192import { homedir, platform } from 'os'import { join } from 'path'import { getFsImplementation } from '../utils/fsOperations.js'import type { IdeType } from './ide.js'
const PLUGIN_PREFIX = 'claude-code-jetbrains-plugin'
// Map of IDE names to their directory patternsconst ideNameToDirMap: { [key: string]: string[] } = { pycharm: ['PyCharm'], intellij: ['IntelliJIdea', 'IdeaIC'], webstorm: ['WebStorm'], phpstorm: ['PhpStorm'], rubymine: ['RubyMine'], clion: ['CLion'], goland: ['GoLand'], rider: ['Rider'], datagrip: ['DataGrip'], appcode: ['AppCode'], dataspell: ['DataSpell'], aqua: ['Aqua'], gateway: ['Gateway'], fleet: ['Fleet'], androidstudio: ['AndroidStudio'],}
// Build plugin directory paths// https://www.jetbrains.com/help/pycharm/directories-used-by-the-ide-to-store-settings-caches-plugins-and-logs.html#plugins-directoryfunction buildCommonPluginDirectoryPaths(ideName: string): string[] { const homeDir = homedir() const directories: string[] = [] const idePatterns = ideNameToDirMap[ideName.toLowerCase()] if (!idePatterns) { return directories }
const appData = process.env.APPDATA || join(homeDir, 'AppData', 'Roaming') const localAppData = process.env.LOCALAPPDATA || join(homeDir, 'AppData', 'Local')
switch (platform()) { case 'darwin': directories.push( join(homeDir, 'Library', 'Application Support', 'JetBrains'), join(homeDir, 'Library', 'Application Support'), ) if (ideName.toLowerCase() === 'androidstudio') { directories.push( join(homeDir, 'Library', 'Application Support', 'Google'), ) } break
case 'win32': directories.push( join(appData, 'JetBrains'), join(localAppData, 'JetBrains'), join(appData), ) if (ideName.toLowerCase() === 'androidstudio') { directories.push(join(localAppData, 'Google')) } break
case 'linux': directories.push( join(homeDir, '.config', 'JetBrains'), join(homeDir, '.local', 'share', 'JetBrains'), ) for (const pattern of idePatterns) { directories.push(join(homeDir, '.' + pattern)) } if (ideName.toLowerCase() === 'androidstudio') { directories.push(join(homeDir, '.config', 'Google')) } break default: break }
return directories}
// Find all actual plugin directories that existasync function detectPluginDirectories(ideName: string): Promise<string[]> { const foundDirectories: string[] = [] const fs = getFsImplementation()
const pluginDirPaths = buildCommonPluginDirectoryPaths(ideName) const idePatterns = ideNameToDirMap[ideName.toLowerCase()] if (!idePatterns) { return foundDirectories }
// Precompile once — idePatterns is invariant across baseDirs const regexes = idePatterns.map(p => new RegExp('^' + p))
for (const baseDir of pluginDirPaths) { try { const entries = await fs.readdir(baseDir) for (const regex of regexes) { for (const entry of entries) { if (!regex.test(entry.name)) continue // Accept symlinks too — dirent.isDirectory() is false for symlinks, // but GNU stow users symlink their JetBrains config dirs. Downstream // fs.stat() calls will filter out symlinks that don't point to dirs. if (!entry.isDirectory() && !entry.isSymbolicLink()) continue const dir = join(baseDir, entry.name) // Linux is the only OS to not have a plugins directory if (platform() === 'linux') { foundDirectories.push(dir) continue } const pluginDir = join(dir, 'plugins') try { await fs.stat(pluginDir) foundDirectories.push(pluginDir) } catch { // Plugin directory doesn't exist, skip } } } } catch { // Ignore errors from stale IDE directories (ENOENT, EACCES, etc.) continue } }
return foundDirectories.filter( (dir, index) => foundDirectories.indexOf(dir) === index, )}
export async function isJetBrainsPluginInstalled( ideType: IdeType,): Promise<boolean> { const pluginDirs = await detectPluginDirectories(ideType) for (const dir of pluginDirs) { const pluginPath = join(dir, PLUGIN_PREFIX) try { await getFsImplementation().stat(pluginPath) return true } catch { // Plugin not found in this directory, continue } } return false}
const pluginInstalledCache = new Map<IdeType, boolean>()const pluginInstalledPromiseCache = new Map<IdeType, Promise<boolean>>()
async function isJetBrainsPluginInstalledMemoized( ideType: IdeType, forceRefresh = false,): Promise<boolean> { if (!forceRefresh) { const existing = pluginInstalledPromiseCache.get(ideType) if (existing) { return existing } } const promise = isJetBrainsPluginInstalled(ideType).then(result => { pluginInstalledCache.set(ideType, result) return result }) pluginInstalledPromiseCache.set(ideType, promise) return promise}
export async function isJetBrainsPluginInstalledCached( ideType: IdeType, forceRefresh = false,): Promise<boolean> { if (forceRefresh) { pluginInstalledCache.delete(ideType) pluginInstalledPromiseCache.delete(ideType) } return isJetBrainsPluginInstalledMemoized(ideType, forceRefresh)}
/** * Returns the cached result of isJetBrainsPluginInstalled synchronously. * Returns false if the result hasn't been resolved yet. * Use this only in sync contexts (e.g., status notice isActive checks). */export function isJetBrainsPluginInstalledCachedSync( ideType: IdeType,): boolean { return pluginInstalledCache.get(ideType) ?? false}