2019-01-23 17:06:53 +01:00
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Copyright ( c ) Microsoft Corporation . All rights reserved .
* Licensed under the MIT License . See License . txt in the project root for license information .
* -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * /
2019-07-15 15:06:26 +02:00
import 'vs/css!./media/extensionsWidgets' ;
2021-07-14 02:19:14 +02:00
import { Disposable , toDisposable , DisposableStore , MutableDisposable , IDisposable } from 'vs/base/common/lifecycle' ;
2021-07-14 11:38:11 +02:00
import { IExtension , IExtensionsWorkbenchService , IExtensionContainer , ExtensionState , ExtensionEditorTab } from 'vs/workbench/contrib/extensions/common/extensions' ;
import { append , $ } from 'vs/base/browser/dom' ;
2019-01-23 17:06:53 +01:00
import * as platform from 'vs/base/common/platform' ;
import { localize } from 'vs/nls' ;
2021-07-14 02:19:14 +02:00
import { EnablementState , IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement' ;
2020-09-25 10:09:32 +02:00
import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations' ;
2019-01-23 17:06:53 +01:00
import { ILabelService } from 'vs/platform/label/common/label' ;
2021-07-22 15:32:18 +02:00
import { extensionButtonProminentBackground , extensionButtonProminentForeground , ExtensionStatusAction , ReloadAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions' ;
import { IThemeService , ThemeIcon , registerThemingParticipant } from 'vs/platform/theme/common/themeService' ;
2019-04-29 11:40:56 +02:00
import { EXTENSION_BADGE_REMOTE_BACKGROUND , EXTENSION_BADGE_REMOTE_FOREGROUND } from 'vs/workbench/common/theme' ;
2021-07-22 10:32:18 +02:00
import { Event } from 'vs/base/common/event' ;
2019-04-16 16:16:16 +02:00
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation' ;
2020-03-29 19:23:46 +02:00
import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge' ;
2020-11-03 17:47:06 +01:00
import { IConfigurationService } from 'vs/platform/configuration/common/configuration' ;
import { IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync' ;
2021-11-23 00:32:43 +01:00
import { activationTimeIcon , errorIcon , infoIcon , installCountIcon , preReleaseIcon , ratingIcon , remoteIcon , starEmptyIcon , starFullIcon , starHalfIcon , syncIgnoredIcon , verifiedPublisherIcon , warningIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons' ;
2021-10-26 00:49:13 +02:00
import { registerColor , textLinkForeground } from 'vs/platform/theme/common/colorRegistry' ;
2021-07-14 02:19:14 +02:00
import { IHoverService } from 'vs/workbench/services/hover/browser/hover' ;
import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget' ;
import { MarkdownString } from 'vs/base/common/htmlContent' ;
import { URI } from 'vs/base/common/uri' ;
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions' ;
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil' ;
import Severity from 'vs/base/common/severity' ;
2021-07-14 11:38:11 +02:00
import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover' ;
2021-07-22 10:32:18 +02:00
import { Color } from 'vs/base/common/color' ;
2019-01-23 17:06:53 +01:00
export abstract class ExtensionWidget extends Disposable implements IExtensionContainer {
2019-08-13 23:55:45 +02:00
private _extension : IExtension | null = null ;
get extension ( ) : IExtension | null { return this . _extension ; }
set extension ( extension : IExtension | null ) { this . _extension = extension ; this . update ( ) ; }
2019-01-23 17:06:53 +01:00
update ( ) : void { this . render ( ) ; }
abstract render ( ) : void ;
}
export class InstallCountWidget extends ExtensionWidget {
constructor (
private container : HTMLElement ,
private small : boolean ,
) {
super ( ) ;
2020-09-08 11:01:43 +02:00
container . classList . add ( 'extension-install-count' ) ;
2019-01-23 17:06:53 +01:00
this . render ( ) ;
}
render ( ) : void {
2020-08-03 19:19:53 +02:00
this . container . innerText = '' ;
2019-01-23 17:06:53 +01:00
if ( ! this . extension ) {
return ;
}
2021-07-14 22:17:26 +02:00
if ( this . small && this . extension . state === ExtensionState . Installed ) {
return ;
}
2021-07-14 02:19:14 +02:00
const installLabel = InstallCountWidget . getInstallLabel ( this . extension , this . small ) ;
if ( ! installLabel ) {
return ;
}
append ( this . container , $ ( 'span' + ThemeIcon . asCSSSelector ( installCountIcon ) ) ) ;
const count = append ( this . container , $ ( 'span.count' ) ) ;
count . textContent = installLabel ;
}
static getInstallLabel ( extension : IExtension , small : boolean ) : string | undefined {
const installCount = extension . installCount ;
2019-01-23 17:06:53 +01:00
if ( installCount === undefined ) {
2021-07-14 02:19:14 +02:00
return undefined ;
2019-01-23 17:06:53 +01:00
}
let installLabel : string ;
2021-07-14 02:19:14 +02:00
if ( small ) {
2019-01-23 17:06:53 +01:00
if ( installCount > 1000000 ) {
installLabel = ` ${ Math . floor ( installCount / 100000 ) / 10 } M ` ;
} else if ( installCount > 1000 ) {
installLabel = ` ${ Math . floor ( installCount / 1000 ) } K ` ;
} else {
installLabel = String ( installCount ) ;
}
}
else {
installLabel = installCount . toLocaleString ( platform . locale ) ;
}
2021-07-14 02:19:14 +02:00
return installLabel ;
2019-01-23 17:06:53 +01:00
}
}
export class RatingsWidget extends ExtensionWidget {
constructor (
private container : HTMLElement ,
private small : boolean
) {
super ( ) ;
2020-09-08 11:01:43 +02:00
container . classList . add ( 'extension-ratings' ) ;
2019-01-23 17:06:53 +01:00
if ( this . small ) {
2020-09-08 11:01:43 +02:00
container . classList . add ( 'small' ) ;
2019-01-23 17:06:53 +01:00
}
this . render ( ) ;
}
render ( ) : void {
2020-08-03 19:19:53 +02:00
this . container . innerText = '' ;
2019-01-23 17:06:53 +01:00
if ( ! this . extension ) {
return ;
}
2021-07-14 22:17:26 +02:00
if ( this . small && this . extension . state === ExtensionState . Installed ) {
return ;
}
2019-01-23 17:06:53 +01:00
if ( this . extension . rating === undefined ) {
return ;
}
if ( this . small && ! this . extension . ratingCount ) {
return ;
}
const rating = Math . round ( this . extension . rating * 2 ) / 2 ;
if ( this . small ) {
2020-11-26 21:58:21 +01:00
append ( this . container , $ ( 'span' + ThemeIcon . asCSSSelector ( starFullIcon ) ) ) ;
2019-01-23 17:06:53 +01:00
const count = append ( this . container , $ ( 'span.count' ) ) ;
count . textContent = String ( rating ) ;
} else {
for ( let i = 1 ; i <= 5 ; i ++ ) {
if ( rating >= i ) {
2020-11-26 21:58:21 +01:00
append ( this . container , $ ( 'span' + ThemeIcon . asCSSSelector ( starFullIcon ) ) ) ;
2019-01-23 17:06:53 +01:00
} else if ( rating >= i - 0.5 ) {
2020-11-26 21:58:21 +01:00
append ( this . container , $ ( 'span' + ThemeIcon . asCSSSelector ( starHalfIcon ) ) ) ;
2019-01-23 17:06:53 +01:00
} else {
2020-11-26 21:58:21 +01:00
append ( this . container , $ ( 'span' + ThemeIcon . asCSSSelector ( starEmptyIcon ) ) ) ;
2019-01-23 17:06:53 +01:00
}
}
2021-07-14 15:28:38 +02:00
if ( this . extension . ratingCount ) {
const ratingCountElemet = append ( this . container , $ ( 'span' , undefined , ` ( ${ this . extension . ratingCount } ) ` ) ) ;
ratingCountElemet . style . paddingLeft = '1px' ;
}
2019-01-23 17:06:53 +01:00
}
}
}
2021-11-23 00:32:43 +01:00
export class PreReleaseIndicatorWidget extends ExtensionWidget {
2021-11-23 21:02:46 +01:00
constructor (
private readonly container : HTMLElement ,
private readonly options : { label : boolean , icon : boolean } ,
) {
2021-11-23 00:32:43 +01:00
super ( ) ;
container . classList . add ( 'extension-pre-release' ) ;
this . render ( ) ;
}
render ( ) : void {
this . container . innerText = '' ;
if ( ! this . extension ) {
return ;
}
2021-11-26 18:10:35 +01:00
if ( ! this . extension . local ? . isPreReleaseVersion && ! this . extension . gallery ? . properties . isPreReleaseVersion ) {
2021-11-23 00:32:43 +01:00
return ;
}
2021-11-23 21:02:46 +01:00
if ( this . options ? . icon ) {
append ( this . container , $ ( 'span' + ThemeIcon . asCSSSelector ( preReleaseIcon ) ) ) ;
}
if ( this . options ? . label ) {
2021-11-25 09:52:04 +01:00
append ( this . container , $ ( 'span.pre-releaselabel' , undefined , localize ( 'pre-release-label' , "Pre-Release" ) ) ) ;
2021-11-23 21:02:46 +01:00
}
2021-11-23 00:32:43 +01:00
}
}
2019-01-23 17:06:53 +01:00
export class RecommendationWidget extends ExtensionWidget {
2019-02-07 23:08:57 +01:00
private element? : HTMLElement ;
2019-07-11 01:25:17 +02:00
private readonly disposables = this . _register ( new DisposableStore ( ) ) ;
2019-01-23 17:06:53 +01:00
constructor (
private parent : HTMLElement ,
2020-04-07 13:10:33 +02:00
@IExtensionRecommendationsService private readonly extensionRecommendationsService : IExtensionRecommendationsService
2019-01-23 17:06:53 +01:00
) {
super ( ) ;
this . render ( ) ;
this . _register ( toDisposable ( ( ) = > this . clear ( ) ) ) ;
2020-09-27 12:09:32 +02:00
this . _register ( this . extensionRecommendationsService . onDidChangeRecommendations ( ( ) = > this . render ( ) ) ) ;
2019-01-23 17:06:53 +01:00
}
private clear ( ) : void {
if ( this . element ) {
this . parent . removeChild ( this . element ) ;
}
2019-02-07 23:08:57 +01:00
this . element = undefined ;
2019-07-11 01:25:17 +02:00
this . disposables . clear ( ) ;
2019-01-23 17:06:53 +01:00
}
render ( ) : void {
this . clear ( ) ;
2021-11-23 00:32:43 +01:00
if ( ! this . extension || this . extension . state === ExtensionState . Installed ) {
2019-01-23 17:06:53 +01:00
return ;
}
2020-04-07 13:10:33 +02:00
const extRecommendations = this . extensionRecommendationsService . getAllRecommendationsWithReason ( ) ;
2019-03-19 15:10:16 +01:00
if ( extRecommendations [ this . extension . identifier . id . toLowerCase ( ) ] ) {
2020-03-29 19:23:46 +02:00
this . element = append ( this . parent , $ ( 'div.extension-bookmark' ) ) ;
2019-03-19 15:10:16 +01:00
const recommendation = append ( this . element , $ ( '.recommendation' ) ) ;
2020-11-26 21:58:21 +01:00
append ( recommendation , $ ( 'span' + ThemeIcon . asCSSSelector ( ratingIcon ) ) ) ;
2019-03-19 15:10:16 +01:00
}
2019-01-23 17:06:53 +01:00
}
}
2021-11-23 00:32:43 +01:00
export class PreReleaseBookmarkWidget extends ExtensionWidget {
private element? : HTMLElement ;
private readonly disposables = this . _register ( new DisposableStore ( ) ) ;
constructor (
private parent : HTMLElement ,
) {
super ( ) ;
this . render ( ) ;
this . _register ( toDisposable ( ( ) = > this . clear ( ) ) ) ;
}
private clear ( ) : void {
if ( this . element ) {
this . parent . removeChild ( this . element ) ;
}
this . element = undefined ;
this . disposables . clear ( ) ;
}
render ( ) : void {
this . clear ( ) ;
if ( ! this . extension ) {
return ;
}
if ( this . extension . hasPreReleaseVersion ) {
this . element = append ( this . parent , $ ( 'div.extension-bookmark' ) ) ;
const preRelease = append ( this . element , $ ( '.pre-release' ) ) ;
append ( preRelease , $ ( 'span' + ThemeIcon . asCSSSelector ( preReleaseIcon ) ) ) ;
}
}
}
2019-01-23 17:06:53 +01:00
export class RemoteBadgeWidget extends ExtensionWidget {
2019-07-11 01:25:17 +02:00
private readonly remoteBadge = this . _register ( new MutableDisposable < RemoteBadge > ( ) ) ;
2019-01-23 17:06:53 +01:00
2019-04-16 16:16:16 +02:00
private element : HTMLElement ;
2019-01-23 17:06:53 +01:00
constructor (
2019-04-16 16:16:16 +02:00
parent : HTMLElement ,
2019-04-23 14:46:59 +02:00
private readonly tooltip : boolean ,
2019-01-23 17:06:53 +01:00
@IExtensionManagementServerService private readonly extensionManagementServerService : IExtensionManagementServerService ,
2019-04-16 16:16:16 +02:00
@IInstantiationService private readonly instantiationService : IInstantiationService
2019-01-23 17:06:53 +01:00
) {
super ( ) ;
2019-04-16 16:16:16 +02:00
this . element = append ( parent , $ ( '.extension-remote-badge-container' ) ) ;
2019-01-23 17:06:53 +01:00
this . render ( ) ;
this . _register ( toDisposable ( ( ) = > this . clear ( ) ) ) ;
}
private clear ( ) : void {
2019-07-11 01:25:17 +02:00
if ( this . remoteBadge . value ) {
this . element . removeChild ( this . remoteBadge . value . element ) ;
2019-01-23 17:06:53 +01:00
}
2019-07-11 01:25:17 +02:00
this . remoteBadge . clear ( ) ;
2019-01-23 17:06:53 +01:00
}
render ( ) : void {
this . clear ( ) ;
2019-07-17 00:55:07 +02:00
if ( ! this . extension || ! this . extension . local || ! this . extension . server || ! ( this . extensionManagementServerService . localExtensionManagementServer && this . extensionManagementServerService . remoteExtensionManagementServer ) || this . extension . server !== this . extensionManagementServerService . remoteExtensionManagementServer ) {
2019-01-23 17:06:53 +01:00
return ;
}
2019-07-17 00:55:07 +02:00
this . remoteBadge . value = this . instantiationService . createInstance ( RemoteBadge , this . tooltip ) ;
append ( this . element , this . remoteBadge . value . element ) ;
2019-01-23 17:06:53 +01:00
}
}
2019-04-16 16:16:16 +02:00
class RemoteBadge extends Disposable {
readonly element : HTMLElement ;
constructor (
2019-04-23 14:46:59 +02:00
private readonly tooltip : boolean ,
2019-04-16 16:16:16 +02:00
@ILabelService private readonly labelService : ILabelService ,
@IThemeService private readonly themeService : IThemeService ,
2019-07-17 00:55:07 +02:00
@IExtensionManagementServerService private readonly extensionManagementServerService : IExtensionManagementServerService
2019-04-16 16:16:16 +02:00
) {
super ( ) ;
2020-03-29 19:23:46 +02:00
this . element = $ ( 'div.extension-badge.extension-remote-badge' ) ;
2019-04-16 16:16:16 +02:00
this . render ( ) ;
}
private render ( ) : void {
2020-11-26 21:58:21 +01:00
append ( this . element , $ ( 'span' + ThemeIcon . asCSSSelector ( remoteIcon ) ) ) ;
2019-04-16 16:16:16 +02:00
const applyBadgeStyle = ( ) = > {
if ( ! this . element ) {
return ;
}
2020-03-02 22:18:51 +01:00
const bgColor = this . themeService . getColorTheme ( ) . getColor ( EXTENSION_BADGE_REMOTE_BACKGROUND ) ;
const fgColor = this . themeService . getColorTheme ( ) . getColor ( EXTENSION_BADGE_REMOTE_FOREGROUND ) ;
2019-04-16 16:16:16 +02:00
this . element . style . backgroundColor = bgColor ? bgColor . toString ( ) : '' ;
this . element . style . color = fgColor ? fgColor . toString ( ) : '' ;
} ;
applyBadgeStyle ( ) ;
2020-03-02 22:18:51 +01:00
this . _register ( this . themeService . onDidColorThemeChange ( ( ) = > applyBadgeStyle ( ) ) ) ;
2019-04-16 16:16:16 +02:00
2019-04-23 14:46:59 +02:00
if ( this . tooltip ) {
const updateTitle = ( ) = > {
2019-07-17 00:55:07 +02:00
if ( this . element && this . extensionManagementServerService . remoteExtensionManagementServer ) {
this . element . title = localize ( 'remote extension title' , "Extension in {0}" , this . extensionManagementServerService . remoteExtensionManagementServer . label ) ;
2019-04-23 14:46:59 +02:00
}
} ;
this . _register ( this . labelService . onDidChangeFormatters ( ( ) = > updateTitle ( ) ) ) ;
updateTitle ( ) ;
}
2019-04-16 16:16:16 +02:00
}
2019-08-13 23:55:45 +02:00
}
2020-03-29 19:23:46 +02:00
export class ExtensionPackCountWidget extends ExtensionWidget {
private element : HTMLElement | undefined ;
constructor (
private readonly parent : HTMLElement ,
) {
super ( ) ;
this . render ( ) ;
this . _register ( toDisposable ( ( ) = > this . clear ( ) ) ) ;
}
private clear ( ) : void {
if ( this . element ) {
2020-09-08 11:01:43 +02:00
this . element . remove ( ) ;
2020-03-29 19:23:46 +02:00
}
}
render ( ) : void {
this . clear ( ) ;
2021-07-19 16:08:39 +02:00
if ( ! this . extension || ! ( this . extension . categories ? . some ( category = > category . toLowerCase ( ) === 'extension packs' ) ) || ! this . extension . extensionPack . length ) {
2020-03-29 19:23:46 +02:00
return ;
}
this . element = append ( this . parent , $ ( '.extension-badge.extension-pack-badge' ) ) ;
const countBadge = new CountBadge ( this . element ) ;
countBadge . setCount ( this . extension . extensionPack . length ) ;
}
}
2020-11-03 17:47:06 +01:00
export class SyncIgnoredWidget extends ExtensionWidget {
constructor (
2021-11-23 00:32:43 +01:00
private readonly container : HTMLElement ,
2020-11-03 17:47:06 +01:00
@IConfigurationService private readonly configurationService : IConfigurationService ,
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService : IExtensionsWorkbenchService ,
@IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService : IUserDataAutoSyncEnablementService ,
) {
super ( ) ;
this . _register ( Event . filter ( this . configurationService . onDidChangeConfiguration , e = > e . affectedKeys . includes ( 'settingsSync.ignoredExtensions' ) ) ( ( ) = > this . render ( ) ) ) ;
this . _register ( userDataAutoSyncEnablementService . onDidChangeEnablement ( ( ) = > this . update ( ) ) ) ;
this . render ( ) ;
}
render ( ) : void {
2021-11-23 00:32:43 +01:00
this . container . innerText = '' ;
if ( this . extension && this . extension . state === ExtensionState . Installed && this . userDataAutoSyncEnablementService . isEnabled ( ) && this . extensionsWorkbenchService . isExtensionIgnoredToSync ( this . extension ) ) {
const element = append ( this . container , $ ( 'span.extension-sync-ignored' + ThemeIcon . asCSSSelector ( syncIgnoredIcon ) ) ) ;
element . title = localize ( 'syncingore.label' , "This extension is ignored during sync." ) ;
element . classList . add ( . . . ThemeIcon . asClassNameArray ( syncIgnoredIcon ) ) ;
}
2020-11-03 17:47:06 +01:00
}
}
2021-02-09 18:50:28 +01:00
2021-07-14 02:19:14 +02:00
export class ExtensionActivationStatusWidget extends ExtensionWidget {
constructor (
private readonly container : HTMLElement ,
private readonly small : boolean ,
@IExtensionService extensionService : IExtensionService ,
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService : IExtensionsWorkbenchService ,
) {
super ( ) ;
this . _register ( extensionService . onDidChangeExtensionsStatus ( extensions = > {
if ( this . extension && extensions . some ( e = > areSameExtensions ( { id : e.value } , this . extension ! . identifier ) ) ) {
this . update ( ) ;
}
} ) ) ;
}
render ( ) : void {
this . container . innerText = '' ;
if ( ! this . extension ) {
return ;
}
const extensionStatus = this . extensionsWorkbenchService . getExtensionStatus ( this . extension ) ;
if ( ! extensionStatus || ! extensionStatus . activationTimes ) {
return ;
}
const activationTime = extensionStatus . activationTimes . codeLoadingTime + extensionStatus . activationTimes . activateCallTime ;
if ( this . small ) {
append ( this . container , $ ( 'span' + ThemeIcon . asCSSSelector ( activationTimeIcon ) ) ) ;
const activationTimeElement = append ( this . container , $ ( 'span.activationTime' ) ) ;
activationTimeElement . textContent = ` ${ activationTime } ms ` ;
} else {
const activationTimeElement = append ( this . container , $ ( 'span.activationTime' ) ) ;
2021-07-15 20:24:44 +02:00
activationTimeElement . textContent = ` ${ localize ( 'activation' , "Activation time" ) } ${ extensionStatus . activationTimes . activationReason . startup ? ` ( ${ localize ( 'startup' , "Startup" ) } ) ` : '' } : ${ activationTime } ms ` ;
2021-07-14 02:19:14 +02:00
}
}
}
export type ExtensionHoverOptions = {
position : ( ) = > HoverPosition ;
readonly target : HTMLElement ;
} ;
export class ExtensionHoverWidget extends ExtensionWidget {
private readonly hover = this . _register ( new MutableDisposable < IDisposable > ( ) ) ;
constructor (
private readonly options : ExtensionHoverOptions ,
2021-07-22 15:32:18 +02:00
private readonly extensionStatusAction : ExtensionStatusAction ,
private readonly reloadAction : ReloadAction ,
2021-07-14 02:19:14 +02:00
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService : IExtensionsWorkbenchService ,
@IHoverService private readonly hoverService : IHoverService ,
2021-07-21 11:46:26 +02:00
@IConfigurationService private readonly configurationService : IConfigurationService ,
2021-07-22 10:32:18 +02:00
@IExtensionRecommendationsService private readonly extensionRecommendationsService : IExtensionRecommendationsService ,
@IThemeService private readonly themeService : IThemeService ,
2021-07-14 02:19:14 +02:00
) {
super ( ) ;
}
render ( ) : void {
2021-07-14 11:38:11 +02:00
this . hover . value = undefined ;
if ( this . extension ) {
this . hover . value = setupCustomHover ( {
2021-07-21 11:46:26 +02:00
delay : this.configurationService.getValue < number > ( 'workbench.hover.delay' ) ,
2021-07-14 11:38:11 +02:00
showHover : ( options ) = > {
2021-07-14 11:45:56 +02:00
return this . hoverService . showHover ( {
. . . options ,
hoverPosition : this.options.position ( ) ,
2021-07-28 15:43:25 +02:00
forcePosition : true ,
2021-07-14 11:45:56 +02:00
additionalClasses : [ 'extension-hover' ]
} ) ;
2021-07-14 11:38:11 +02:00
} ,
placement : 'element'
} , this . options . target , { markdown : ( ) = > Promise . resolve ( this . getHoverMarkdown ( ) ) , markdownNotSupportedFallback : undefined } ) ;
}
2021-07-14 02:19:14 +02:00
}
2021-07-14 11:38:11 +02:00
private getHoverMarkdown ( ) : MarkdownString | undefined {
2021-07-14 02:19:14 +02:00
if ( ! this . extension ) {
2021-07-14 11:38:11 +02:00
return undefined ;
2021-07-14 02:19:14 +02:00
}
const markdown = new MarkdownString ( '' , { isTrusted : true , supportThemeIcons : true } ) ;
2021-07-14 15:28:38 +02:00
2021-07-15 16:24:05 +02:00
markdown . appendMarkdown ( ` ** ${ this . extension . displayName } ** _v ${ this . extension . version } _ ` ) ;
2021-11-26 18:10:35 +01:00
if ( this . extension . local ? . isPreReleaseVersion || this . extension . gallery ? . properties . isPreReleaseVersion ) {
2021-11-23 00:32:43 +01:00
const extensionPreReleaseIcon = this . themeService . getColorTheme ( ) . getColor ( extensionPreReleaseIconColor ) ;
2021-11-26 18:10:35 +01:00
markdown . appendMarkdown ( ` <span style="color:#ffffff;background-color: ${ extensionPreReleaseIcon ? Color . Format . CSS . formatHex ( extensionPreReleaseIcon ) : '#ffffff' } ;"> ${ localize ( 'pre-release-label' , "Pre-Release" ) } </span> ` ) ;
2021-11-23 00:32:43 +01:00
}
2021-07-14 02:19:14 +02:00
markdown . appendText ( ` \ n ` ) ;
2021-07-27 22:11:50 +02:00
if ( this . extension . description ) {
markdown . appendMarkdown ( ` ${ this . extension . description } ` ) ;
markdown . appendText ( ` \ n ` ) ;
}
2021-10-24 12:25:08 +02:00
if ( this . extension . publisherDomain ? . verified ) {
const bgColor = this . themeService . getColorTheme ( ) . getColor ( extensionVerifiedPublisherIconColor ) ;
2021-10-25 17:58:54 +02:00
const publisherVerifiedTooltip = localize ( 'publisher verified tooltip' , "This publisher has verified ownership of {0}" , ` [ ${ URI . parse ( this . extension . publisherDomain . link ) . authority } ]( ${ this . extension . publisherDomain . link } ) ` ) ;
markdown . appendMarkdown ( ` <span style="color: ${ bgColor ? Color . Format . CSS . formatHex ( bgColor ) : '#ffffff' } ;"> $ ( ${ verifiedPublisherIcon . id } )</span> ${ publisherVerifiedTooltip } ` ) ;
2021-10-24 12:25:08 +02:00
markdown . appendText ( ` \ n ` ) ;
}
2021-11-26 20:02:54 +01:00
const preReleaseMessage = ExtensionHoverWidget . getPreReleaseMessage ( this . extension ) ;
2021-07-22 15:32:18 +02:00
const extensionRuntimeStatus = this . extensionsWorkbenchService . getExtensionStatus ( this . extension ) ;
const extensionStatus = this . extensionStatusAction . status ;
const reloadRequiredMessage = this . reloadAction . enabled ? this . reloadAction . tooltip : '' ;
2021-07-27 22:11:50 +02:00
const recommendationMessage = this . getRecommendationMessage ( this . extension ) ;
2021-11-26 20:09:01 +01:00
if ( extensionRuntimeStatus || extensionStatus || reloadRequiredMessage || recommendationMessage || preReleaseMessage ) {
2021-07-27 22:11:50 +02:00
markdown . appendMarkdown ( ` --- ` ) ;
markdown . appendText ( ` \ n ` ) ;
2021-07-14 02:19:14 +02:00
2021-07-22 15:32:18 +02:00
if ( extensionRuntimeStatus ) {
if ( extensionRuntimeStatus . activationTimes ) {
const activationTime = extensionRuntimeStatus . activationTimes . codeLoadingTime + extensionRuntimeStatus . activationTimes . activateCallTime ;
2021-07-27 16:57:08 +02:00
markdown . appendMarkdown ( ` ${ localize ( 'activation' , "Activation time" ) } ${ extensionRuntimeStatus . activationTimes . activationReason . startup ? ` ( ${ localize ( 'startup' , "Startup" ) } ) ` : '' } : \` ${ activationTime } ms \` ` ) ;
2021-07-14 02:44:16 +02:00
markdown . appendText ( ` \ n ` ) ;
}
2021-07-22 15:32:18 +02:00
if ( extensionRuntimeStatus . runtimeErrors . length || extensionRuntimeStatus . messages . length ) {
const hasErrors = extensionRuntimeStatus . runtimeErrors . length || extensionRuntimeStatus . messages . some ( message = > message . type === Severity . Error ) ;
const hasWarnings = extensionRuntimeStatus . messages . some ( message = > message . type === Severity . Warning ) ;
const errorsLink = extensionRuntimeStatus . runtimeErrors . length ? ` [ ${ extensionRuntimeStatus . runtimeErrors . length === 1 ? localize ( 'uncaught error' , '1 uncaught error' ) : localize ( 'uncaught errors' , '{0} uncaught errors' , extensionRuntimeStatus . runtimeErrors . length ) } ]( ${ URI . parse ( ` command:extension.open? ${ encodeURIComponent ( JSON . stringify ( [ this . extension . identifier . id , ExtensionEditorTab . RuntimeStatus ] ) ) } ` ) } ) ` : undefined ;
const messageLink = extensionRuntimeStatus . messages . length ? ` [ ${ extensionRuntimeStatus . messages . length === 1 ? localize ( 'message' , '1 message' ) : localize ( 'messages' , '{0} messages' , extensionRuntimeStatus . messages . length ) } ]( ${ URI . parse ( ` command:extension.open? ${ encodeURIComponent ( JSON . stringify ( [ this . extension . identifier . id , ExtensionEditorTab . RuntimeStatus ] ) ) } ` ) } ) ` : undefined ;
2021-07-15 16:24:05 +02:00
markdown . appendMarkdown ( ` $ ( ${ hasErrors ? errorIcon.id : hasWarnings ? warningIcon.id : infoIcon.id } ) This extension has reported ` ) ;
if ( errorsLink && messageLink ) {
markdown . appendMarkdown ( ` ${ errorsLink } and ${ messageLink } ` ) ;
} else {
markdown . appendMarkdown ( ` ${ errorsLink || messageLink } ` ) ;
}
markdown . appendText ( ` \ n ` ) ;
2021-07-14 02:44:16 +02:00
}
}
2021-07-15 16:24:05 +02:00
2021-07-22 15:32:18 +02:00
if ( extensionStatus ) {
if ( extensionStatus . icon ) {
markdown . appendMarkdown ( ` $ ( ${ extensionStatus . icon . id } ) ` ) ;
}
2021-07-22 16:13:39 +02:00
markdown . appendMarkdown ( extensionStatus . message . value ) ;
2021-07-22 15:32:18 +02:00
if ( this . extension . enablementState === EnablementState . DisabledByExtensionDependency && this . extension . local ) {
markdown . appendMarkdown ( ` [ ${ localize ( 'dependencies' , "Show Dependencies" ) } ]( ${ URI . parse ( ` command:extension.open? ${ encodeURIComponent ( JSON . stringify ( [ this . extension . identifier . id , ExtensionEditorTab . Dependencies ] ) ) } ` ) } ) ` ) ;
}
markdown . appendText ( ` \ n ` ) ;
}
if ( reloadRequiredMessage ) {
markdown . appendMarkdown ( ` $ ( ${ infoIcon . id } ) ` ) ;
markdown . appendMarkdown ( ` ${ reloadRequiredMessage } ` ) ;
markdown . appendText ( ` \ n ` ) ;
}
2021-11-26 20:09:01 +01:00
if ( preReleaseMessage ) {
const extensionPreReleaseIcon = this . themeService . getColorTheme ( ) . getColor ( extensionPreReleaseIconColor ) ;
markdown . appendMarkdown ( ` <span style="color: ${ extensionPreReleaseIcon ? Color . Format . CSS . formatHex ( extensionPreReleaseIcon ) : '#ffffff' } ;"> $ ( ${ preReleaseIcon . id } )</span> ${ preReleaseMessage } ` ) ;
markdown . appendText ( ` \ n ` ) ;
}
2021-07-27 22:11:50 +02:00
if ( recommendationMessage ) {
markdown . appendMarkdown ( recommendationMessage ) ;
markdown . appendText ( ` \ n ` ) ;
}
2021-07-22 10:32:18 +02:00
}
2021-07-14 11:38:11 +02:00
return markdown ;
2021-07-14 02:19:14 +02:00
}
2021-07-22 10:32:18 +02:00
private getRecommendationMessage ( extension : IExtension ) : string | undefined {
2021-11-23 00:32:43 +01:00
if ( extension . state === ExtensionState . Installed ) {
return undefined ;
}
2021-07-22 10:32:18 +02:00
const recommendation = this . extensionRecommendationsService . getAllRecommendationsWithReason ( ) [ extension . identifier . id . toLowerCase ( ) ] ;
2021-11-23 00:32:43 +01:00
if ( ! recommendation ? . reasonText ) {
return undefined ;
2021-07-14 02:19:14 +02:00
}
2021-11-23 00:32:43 +01:00
const bgColor = this . themeService . getColorTheme ( ) . getColor ( extensionButtonProminentBackground ) ;
return ` <span style="color: ${ bgColor ? Color . Format . CSS . formatHex ( bgColor ) : '#ffffff' } ;"> $ ( ${ starEmptyIcon . id } )</span> ${ recommendation . reasonText } ` ;
}
2021-11-26 20:02:54 +01:00
static getPreReleaseMessage ( extension : IExtension ) : string | undefined {
2021-11-23 00:32:43 +01:00
if ( ! extension . hasPreReleaseVersion ) {
return undefined ;
}
2021-11-26 18:10:35 +01:00
if ( extension . local ? . isPreReleaseVersion || extension . gallery ? . properties . isPreReleaseVersion ) {
2021-11-23 00:32:43 +01:00
return undefined ;
}
2021-11-25 09:52:04 +01:00
const preReleaseVersionLink = ` [ ${ localize ( 'Show prerelease version' , "Pre-Release version" ) } ]( ${ URI . parse ( ` command:workbench.extensions.action.showPreReleaseVersion? ${ encodeURIComponent ( JSON . stringify ( [ extension . identifier . id ] ) ) } ` ) } ) ` ;
2021-11-26 20:02:54 +01:00
return localize ( 'has prerelease' , "This extension has a {0} available" , preReleaseVersionLink ) ;
2021-07-14 02:19:14 +02:00
}
2021-07-22 10:32:18 +02:00
2021-07-14 02:19:14 +02:00
}
2021-02-09 18:50:28 +01:00
// Rating icon
export const extensionRatingIconColor = registerColor ( 'extensionIcon.starForeground' , { light : '#DF6100' , dark : '#FF8E00' , hc : '#FF8E00' } , localize ( 'extensionIconStarForeground' , "The icon color for extension ratings." ) , true ) ;
2021-10-26 00:49:13 +02:00
export const extensionVerifiedPublisherIconColor = registerColor ( 'extensionIcon.verifiedForeground' , { dark : textLinkForeground , light : textLinkForeground , hc : textLinkForeground } , localize ( 'extensionIconVerifiedForeground' , "The icon color for extension verified publisher." ) , true ) ;
2021-11-23 00:32:43 +01:00
export const extensionPreReleaseIconColor = registerColor ( 'extensionIcon.preReleaseForeground' , { dark : '#1d9271' , light : '#1d9271' , hc : '#1d9271' } , localize ( 'extensionPreReleaseForeground' , "The icon color for pre-release extension." ) , true ) ;
2021-02-09 18:50:28 +01:00
registerThemingParticipant ( ( theme , collector ) = > {
const extensionRatingIcon = theme . getColor ( extensionRatingIconColor ) ;
if ( extensionRatingIcon ) {
collector . addRule ( ` .extension-ratings .codicon-extensions-star-full, .extension-ratings .codicon-extensions-star-half { color: ${ extensionRatingIcon } ; } ` ) ;
}
2021-07-22 15:32:18 +02:00
const fgColor = theme . getColor ( extensionButtonProminentForeground ) ;
if ( fgColor ) {
collector . addRule ( ` .extension-bookmark .recommendation { color: ${ fgColor } ; } ` ) ;
}
const bgColor = theme . getColor ( extensionButtonProminentBackground ) ;
if ( bgColor ) {
collector . addRule ( ` .extension-bookmark .recommendation { border-top-color: ${ bgColor } ; } ` ) ;
2021-07-22 23:58:50 +02:00
collector . addRule ( ` .monaco-workbench .extension-editor > .header > .details > .recommendation .codicon { color: ${ bgColor } ; } ` ) ;
2021-07-22 15:32:18 +02:00
}
2021-10-24 12:25:08 +02:00
const extensionVerifiedPublisherIcon = theme . getColor ( extensionVerifiedPublisherIconColor ) ;
if ( extensionVerifiedPublisherIcon ) {
collector . addRule ( ` ${ ThemeIcon . asCSSSelector ( verifiedPublisherIcon ) } { color: ${ extensionVerifiedPublisherIcon } ; } ` ) ;
}
2021-11-23 00:32:43 +01:00
2021-02-09 18:50:28 +01:00
} ) ;