2016-04-19 09:37:45 +02: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 17:51:10 +02:00
import 'vs/css!./media/extensionEditor' ;
2016-06-23 19:01:03 +02:00
import { localize } from 'vs/nls' ;
2018-09-06 20:23:40 +02:00
import { createCancelablePromise } from 'vs/base/common/async' ;
2016-08-24 10:47:52 +02:00
import * as arrays from 'vs/base/common/arrays' ;
2017-04-05 17:47:58 +02:00
import { OS } from 'vs/base/common/platform' ;
2018-12-11 10:14:26 +01:00
import { Event , Emitter } from 'vs/base/common/event' ;
2018-09-06 17:38:35 +02:00
import { Cache , CacheResult } from 'vs/base/common/cache' ;
2020-02-06 11:18:21 +01:00
import { Action , IAction } from 'vs/base/common/actions' ;
2021-07-14 14:18:32 +02:00
import { getErrorMessage , isPromiseCanceledError , onUnexpectedError } from 'vs/base/common/errors' ;
2021-11-23 00:32:43 +01:00
import { dispose , toDisposable , Disposable , DisposableStore , IDisposable , MutableDisposable } from 'vs/base/common/lifecycle' ;
2021-07-26 11:23:08 +02:00
import { append , $ , finalHandler , join , addDisposableListener , EventType , setParentFlowTo , reset , Dimension } from 'vs/base/browser/dom' ;
2020-08-24 08:30:15 +02:00
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane' ;
2016-04-19 09:37:45 +02:00
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry' ;
2017-08-28 15:24:32 +02:00
import { IInstantiationService , ServicesAccessor } from 'vs/platform/instantiation/common/instantiation' ;
2020-09-27 12:09:32 +02:00
import { IExtensionIgnoredRecommendationsService , IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations' ;
2020-10-09 16:38:27 +02:00
import { IExtensionManifest , IKeyBinding , IView , IViewContainer } from 'vs/platform/extensions/common/extensions' ;
2021-10-18 20:34:32 +02:00
import { KeyMod , KeyCode } from 'vs/base/common/keyCodes' ;
import { ResolvedKeybinding } from 'vs/base/common/keybindings' ;
2021-11-23 00:32:43 +01:00
import { ExtensionsInput , IExtensionEditorOptions } from 'vs/workbench/contrib/extensions/common/extensionsInput' ;
2021-07-22 23:31:04 +02:00
import { IExtensionsWorkbenchService , IExtensionsViewPaneContainer , VIEWLET_ID , IExtension , ExtensionContainers , ExtensionEditorTab , ExtensionState } from 'vs/workbench/contrib/extensions/common/extensions' ;
2021-11-26 20:02:54 +01:00
import { RatingsWidget , InstallCountWidget , RemoteBadgeWidget , PreReleaseIndicatorWidget , ExtensionHoverWidget } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets' ;
2021-05-25 17:12:49 +02:00
import { IEditorOpenContext } from 'vs/workbench/common/editor' ;
2016-06-21 17:26:15 +02:00
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar' ;
2020-11-09 16:18:23 +01:00
import {
2021-07-05 15:58:05 +02:00
UpdateAction , ReloadAction , EnableDropDownAction , DisableDropDownAction , ExtensionStatusLabelAction , SetFileIconThemeAction , SetColorThemeAction ,
2021-07-22 15:32:18 +02:00
RemoteInstallAction , ExtensionStatusAction , LocalInstallAction , ToggleSyncExtensionAction , SetProductIconThemeAction ,
2020-11-09 16:18:23 +01:00
ActionWithDropDownAction , InstallDropdownAction , InstallingLabelAction , UninstallAction , ExtensionActionWithDropdownActionViewItem , ExtensionDropDownAction ,
2021-11-26 02:02:41 +01:00
InstallAnotherVersionAction , ExtensionEditorManageExtensionAction , WebInstallAction , SwitchToPreReleaseVersionAction , SwitchToReleasedVersionAction , SwitchUnsupportedExtensionToPreReleaseExtensionAction
2020-11-09 16:18:23 +01:00
} from 'vs/workbench/contrib/extensions/browser/extensionsActions' ;
2016-10-08 09:53:13 +02:00
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding' ;
2016-08-24 16:07:48 +02:00
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement' ;
2019-11-29 15:51:37 +01:00
import { IOpenerService , matchesScheme } from 'vs/platform/opener/common/opener' ;
2021-07-14 14:18:32 +02:00
import { IColorTheme , ICssStyleCollector , IThemeService , registerThemingParticipant , ThemeIcon } from 'vs/platform/theme/common/themeService' ;
2017-04-05 17:47:58 +02:00
import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel' ;
2021-11-23 00:32:43 +01:00
import { ContextKeyExpr , IContextKey , IContextKeyService , RawContextKey } from 'vs/platform/contextkey/common/contextkey' ;
2018-05-20 13:35:34 +02:00
import { IEditorService } from 'vs/workbench/services/editor/common/editorService' ;
2018-07-24 18:09:42 +02:00
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry' ;
2017-09-11 15:33:06 +02:00
import { Color } from 'vs/base/common/color' ;
2021-07-14 14:18:32 +02:00
import { INotificationService , Severity } from 'vs/platform/notification/common/notification' ;
2020-11-11 08:17:27 +01:00
import { CancellationToken , CancellationTokenSource } from 'vs/base/common/cancellation' ;
2021-03-29 16:24:46 +02:00
import { ExtensionsTree , ExtensionData , ExtensionsGridView , getExtensions } from 'vs/workbench/contrib/extensions/browser/extensionsViewer' ;
2019-07-15 17:51:10 +02:00
import { ShowCurrentReleaseNotesActionId } from 'vs/workbench/contrib/update/common/update' ;
2018-10-13 18:00:36 +02:00
import { IStorageService } from 'vs/platform/storage/common/storage' ;
2018-11-29 16:05:06 +01:00
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions' ;
2018-11-29 17:08:55 +01:00
import { getDefaultValue } from 'vs/platform/configuration/common/configurationRegistry' ;
2021-07-22 16:13:39 +02:00
import { isUndefined } from 'vs/base/common/types' ;
2019-02-12 10:13:07 +01:00
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService' ;
2021-11-05 01:29:42 +01:00
import { IWebviewService , IWebview , KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/webview/browser/webview' ;
2019-07-29 17:36:32 +02:00
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent' ;
2019-08-13 00:32:21 +02:00
import { generateUuid } from 'vs/base/common/uuid' ;
2019-08-15 14:37:08 +02:00
import { platform } from 'vs/base/common/process' ;
2019-08-20 11:28:55 +02:00
import { URI } from 'vs/base/common/uri' ;
2019-09-20 00:44:57 +02:00
import { Schemas } from 'vs/base/common/network' ;
2021-09-03 21:17:02 +02:00
import { DEFAULT_MARKDOWN_STYLES , renderMarkdownDocument } from 'vs/workbench/contrib/markdown/browser/markdownDocumentRenderer' ;
2019-09-20 01:51:11 +02:00
import { IModeService } from 'vs/editor/common/services/modeService' ;
import { TokenizationRegistry } from 'vs/editor/common/modes' ;
import { generateTokensCSSForColorMap } from 'vs/editor/common/modes/supports/tokenization' ;
2021-07-15 00:01:13 +02:00
import { buttonForeground , buttonHoverBackground , editorBackground , textLinkActiveForeground , textLinkForeground } from 'vs/platform/theme/common/colorRegistry' ;
2020-02-06 10:10:21 +01:00
import { registerAction2 , Action2 } from 'vs/platform/actions/common/actions' ;
2020-10-22 00:20:25 +02:00
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView' ;
2021-02-26 14:08:37 +01:00
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys' ;
2021-03-13 02:13:19 +01:00
import { Delegate } from 'vs/workbench/contrib/extensions/browser/extensionsList' ;
2020-04-22 16:32:28 +02:00
import { renderMarkdown } from 'vs/base/browser/markdownRenderer' ;
2021-04-13 19:56:05 +02:00
import { attachKeybindingLabelStyler } from 'vs/platform/theme/common/styler' ;
2021-07-14 14:18:32 +02:00
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil' ;
2021-11-26 20:02:54 +01:00
import { errorIcon , infoIcon , preReleaseIcon , starEmptyIcon , verifiedPublisherIcon as verifiedPublisherThemeIcon , warningIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons' ;
2021-07-22 15:32:18 +02:00
import { MarkdownString } from 'vs/base/common/htmlContent' ;
2021-09-21 18:44:43 +02:00
import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite' ;
import { ViewContainerLocation } from 'vs/workbench/common/views' ;
2017-07-07 22:20:53 +02:00
2019-06-04 23:33:14 +02:00
class NavBar extends Disposable {
2016-08-22 16:18:32 +02:00
2019-06-04 23:33:14 +02:00
private _onChange = this . _register ( new Emitter < { id : string | null , focus : boolean } > ( ) ) ;
2019-03-05 19:34:30 +01:00
get onChange ( ) : Event < { id : string | null , focus : boolean } > { return this . _onChange . event ; }
2016-08-22 16:18:32 +02:00
2020-03-04 09:26:14 +01:00
private _currentId : string | null = null ;
get currentId ( ) : string | null { return this . _currentId ; }
2016-08-22 16:18:32 +02:00
private actions : Action [ ] ;
private actionbar : ActionBar ;
constructor ( container : HTMLElement ) {
2019-06-04 23:33:14 +02:00
super ( ) ;
2016-08-22 16:18:32 +02:00
const element = append ( container , $ ( '.navbar' ) ) ;
this . actions = [ ] ;
2019-06-04 23:33:14 +02:00
this . actionbar = this . _register ( new ActionBar ( element , { animated : false } ) ) ;
2016-08-22 16:18:32 +02:00
}
2018-05-14 14:14:19 +02:00
push ( id : string , label : string , tooltip : string ) : void {
2021-10-06 18:17:37 +02:00
const action = new Action ( id , label , undefined , true , ( ) = > this . update ( id , true ) ) ;
2016-08-22 16:18:32 +02:00
2018-05-16 13:49:31 +02:00
action . tooltip = tooltip ;
2018-05-14 14:14:19 +02:00
2016-08-22 16:18:32 +02:00
this . actions . push ( action ) ;
this . actionbar . push ( action ) ;
if ( this . actions . length === 1 ) {
2021-10-06 18:17:37 +02:00
this . update ( id ) ;
2016-08-22 16:18:32 +02:00
}
}
clear ( ) : void {
this . actions = dispose ( this . actions ) ;
this . actionbar . clear ( ) ;
}
2021-10-06 18:17:37 +02:00
switch ( id : string ) : boolean {
const action = this . actions . find ( action = > action . id === id ) ;
if ( action ) {
action . run ( ) ;
return true ;
}
return false ;
2016-11-01 11:35:43 +01:00
}
2021-10-06 18:17:37 +02:00
private update ( id : string , focus? : boolean ) : void {
2020-03-04 09:26:14 +01:00
this . _currentId = id ;
2019-02-14 23:33:35 +01:00
this . _onChange . fire ( { id , focus : ! ! focus } ) ;
2019-07-05 17:11:25 +02:00
this . actions . forEach ( a = > a . checked = a . id === id ) ;
2016-11-01 11:35:43 +01:00
}
2016-08-22 16:18:32 +02:00
}
2016-08-24 16:07:48 +02:00
interface ILayoutParticipant {
layout ( ) : void ;
}
2018-09-12 16:20:43 +02:00
interface IActiveElement {
focus ( ) : void ;
}
2019-08-14 01:09:36 +02:00
interface IExtensionEditorTemplate {
iconContainer : HTMLElement ;
icon : HTMLImageElement ;
name : HTMLElement ;
preview : HTMLElement ;
builtin : HTMLElement ;
2020-04-06 10:08:03 +02:00
version : HTMLElement ;
2021-11-23 00:32:43 +01:00
preRelease : HTMLElement ;
2019-08-14 01:09:36 +02:00
publisher : HTMLElement ;
2021-10-24 12:25:08 +02:00
publisherDisplayName : HTMLElement ;
verifiedPublisherIcon : HTMLElement ;
2019-08-14 01:09:36 +02:00
installCount : HTMLElement ;
rating : HTMLElement ;
description : HTMLElement ;
2021-07-22 23:31:04 +02:00
actionsAndStatusContainer : HTMLElement ;
2019-08-14 01:09:36 +02:00
extensionActionBar : ActionBar ;
2021-07-22 15:32:18 +02:00
status : HTMLElement ;
2021-11-26 20:02:54 +01:00
preReleaseText : HTMLElement ;
2021-07-22 15:32:18 +02:00
recommendation : HTMLElement ;
2019-08-14 01:09:36 +02:00
navbar : NavBar ;
content : HTMLElement ;
header : HTMLElement ;
}
2020-12-18 22:56:42 +01:00
const enum WebviewIndex {
Readme ,
Changelog
}
2021-11-23 00:32:43 +01:00
const CONTEXT_SHOW_PRE_RELEASE_VERSION = new RawContextKey < boolean > ( 'showPreReleaseVersion' , false ) ;
2020-08-24 08:30:15 +02:00
export class ExtensionEditor extends EditorPane {
2016-04-19 09:37:45 +02:00
2018-02-15 08:27:02 +01:00
static readonly ID : string = 'workbench.editor.extension' ;
2016-04-19 09:37:45 +02:00
2021-11-23 00:32:43 +01:00
private readonly _scopedContextKeyService = this . _register ( new MutableDisposable < IContextKeyService > ( ) ) ;
2019-08-14 01:09:36 +02:00
private template : IExtensionEditorTemplate | undefined ;
2016-04-19 09:37:45 +02:00
2019-02-07 19:17:27 +01:00
private extensionReadme : Cache < string > | null ;
private extensionChangelog : Cache < string > | null ;
private extensionManifest : Cache < IExtensionManifest | null > | null ;
2016-08-23 16:05:17 +02:00
2020-12-18 22:56:42 +01:00
// Some action bar items use a webview whose vertical scroll position we track in this map
private initialScrollProgress : Map < WebviewIndex , number > = new Map ( ) ;
// Spot when an ExtensionEditor instance gets reused for a different extension, in which case the vertical scroll positions must be zeroed
private currentIdentifier : string = '' ;
2016-08-24 16:07:48 +02:00
private layoutParticipants : ILayoutParticipant [ ] = [ ] ;
2019-06-12 20:49:11 +02:00
private readonly contentDisposables = this . _register ( new DisposableStore ( ) ) ;
private readonly transientDisposables = this . _register ( new DisposableStore ( ) ) ;
2019-08-14 01:09:36 +02:00
private activeElement : IActiveElement | null = null ;
2018-04-29 04:24:58 +02:00
private editorLoadComplete : boolean = false ;
2021-07-26 11:23:08 +02:00
private dimension : Dimension | undefined ;
2016-04-19 09:37:45 +02:00
2021-11-23 00:32:43 +01:00
private showPreReleaseVersionContextKey : IContextKey < boolean > | undefined ;
2016-04-19 09:37:45 +02:00
constructor (
@ITelemetryService telemetryService : ITelemetryService ,
2018-02-21 00:58:37 +01:00
@IInstantiationService private readonly instantiationService : IInstantiationService ,
2021-09-21 18:44:43 +02:00
@IPaneCompositePartService private readonly paneCompositeService : IPaneCompositePartService ,
2018-02-21 00:58:37 +01:00
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService : IExtensionsWorkbenchService ,
2021-04-08 19:34:28 +02:00
@IThemeService themeService : IThemeService ,
2018-02-21 00:58:37 +01:00
@IKeybindingService private readonly keybindingService : IKeybindingService ,
@INotificationService private readonly notificationService : INotificationService ,
@IOpenerService private readonly openerService : IOpenerService ,
2020-04-07 13:10:33 +02:00
@IExtensionRecommendationsService private readonly extensionRecommendationsService : IExtensionRecommendationsService ,
2020-09-27 12:09:32 +02:00
@IExtensionIgnoredRecommendationsService private readonly extensionIgnoredRecommendationsService : IExtensionIgnoredRecommendationsService ,
2018-11-29 16:05:06 +01:00
@IStorageService storageService : IStorageService ,
2019-02-12 10:13:07 +01:00
@IExtensionService private readonly extensionService : IExtensionService ,
2019-07-15 12:57:52 +02:00
@IWorkbenchThemeService private readonly workbenchThemeService : IWorkbenchThemeService ,
2019-09-20 01:51:11 +02:00
@IWebviewService private readonly webviewService : IWebviewService ,
@IModeService private readonly modeService : IModeService ,
2020-10-22 00:20:25 +02:00
@IContextMenuService private readonly contextMenuService : IContextMenuService ,
2021-11-23 00:32:43 +01:00
@IContextKeyService private readonly contextKeyService : IContextKeyService ,
2016-04-19 09:37:45 +02:00
) {
2018-10-13 17:54:44 +02:00
super ( ExtensionEditor . ID , telemetryService , themeService , storageService ) ;
2016-08-23 16:05:17 +02:00
this . extensionReadme = null ;
2016-09-14 09:31:11 +02:00
this . extensionChangelog = null ;
2016-08-23 16:05:17 +02:00
this . extensionManifest = null ;
2016-04-19 09:37:45 +02:00
}
2021-11-23 00:32:43 +01:00
override get scopedContextKeyService ( ) : IContextKeyService | undefined {
return this . _scopedContextKeyService . value ;
}
2018-04-04 10:08:57 +02:00
createEditor ( parent : HTMLElement ) : void {
const root = append ( parent , $ ( '.extension-editor' ) ) ;
2021-11-23 00:32:43 +01:00
this . _scopedContextKeyService . value = this . contextKeyService . createScoped ( root ) ;
this . _scopedContextKeyService . value . createKey ( 'inExtensionEditor' , true ) ;
this . showPreReleaseVersionContextKey = CONTEXT_SHOW_PRE_RELEASE_VERSION . bindTo ( this . _scopedContextKeyService . value ) ;
2019-04-08 12:42:00 +02:00
root . tabIndex = 0 ; // this is required for the focus tracker on the editor
root . style . outline = 'none' ;
2020-03-25 12:35:15 +01:00
root . setAttribute ( 'role' , 'document' ) ;
2019-08-14 01:09:36 +02:00
const header = append ( root , $ ( '.header' ) ) ;
2016-06-20 18:05:53 +02:00
2019-08-14 01:09:36 +02:00
const iconContainer = append ( header , $ ( '.icon-container' ) ) ;
const icon = append ( iconContainer , $ < HTMLImageElement > ( 'img.icon' , { draggable : false } ) ) ;
2016-06-20 18:05:53 +02:00
2019-08-14 01:09:36 +02:00
const details = append ( header , $ ( '.details' ) ) ;
2016-06-23 19:01:03 +02:00
const title = append ( details , $ ( '.title' ) ) ;
2020-04-29 20:00:23 +02:00
const name = append ( title , $ ( 'span.name.clickable' , { title : localize ( 'name' , "Extension name" ) , role : 'heading' , tabIndex : 0 } ) ) ;
2021-07-17 09:37:07 +02:00
const version = append ( title , $ ( 'code.version' , { title : localize ( 'extension version' , "Extension Version" ) } ) ) ;
2018-02-19 17:27:35 +01:00
2019-08-14 01:09:36 +02:00
const preview = append ( title , $ ( 'span.preview' , { title : localize ( 'preview' , "Preview" ) } ) ) ;
preview . textContent = localize ( 'preview' , "Preview" ) ;
2018-02-19 17:27:35 +01:00
2019-08-14 01:09:36 +02:00
const builtin = append ( title , $ ( 'span.builtin' ) ) ;
builtin . textContent = localize ( 'builtin' , "Built-in" ) ;
2021-11-23 00:32:43 +01:00
const preRelease = append ( title , $ ( 'span.pre-release' ) ) ;
2016-06-21 10:55:06 +02:00
const subtitle = append ( details , $ ( '.subtitle' ) ) ;
2021-10-24 12:25:08 +02:00
const publisher = append ( append ( subtitle , $ ( '.subtitle-entry' ) ) , $ ( '.publisher.clickable' , { title : localize ( 'publisher' , "Publisher" ) , tabIndex : 0 } ) ) ;
2021-09-17 15:51:28 +02:00
publisher . setAttribute ( 'role' , 'button' ) ;
2021-10-24 12:25:08 +02:00
const verifiedPublisherIcon = append ( publisher , $ ( ` .publisher-verified ${ ThemeIcon . asCSSSelector ( verifiedPublisherThemeIcon ) } ` ) ) ;
const publisherDisplayName = append ( publisher , $ ( '.publisher-name' ) ) ;
2020-07-01 16:17:36 +02:00
const installCount = append ( append ( subtitle , $ ( '.subtitle-entry' ) ) , $ ( 'span.install' , { title : localize ( 'install count' , "Install count" ) , tabIndex : 0 } ) ) ;
const rating = append ( append ( subtitle , $ ( '.subtitle-entry' ) ) , $ ( 'span.rating.clickable' , { title : localize ( 'rating' , "Rating" ) , tabIndex : 0 } ) ) ;
2021-09-17 15:51:28 +02:00
rating . setAttribute ( 'role' , 'link' ) ; // #132645
2020-04-06 10:08:03 +02:00
2019-08-14 01:09:36 +02:00
const description = append ( details , $ ( '.description' ) ) ;
2016-06-21 17:26:15 +02:00
2021-07-22 23:31:04 +02:00
const actionsAndStatusContainer = append ( details , $ ( '.actions-status-container' ) ) ;
const extensionActionBar = this . _register ( new ActionBar ( actionsAndStatusContainer , {
2016-10-19 13:04:42 +02:00
animated : false ,
2020-02-06 11:18:21 +01:00
actionViewItemProvider : ( action : IAction ) = > {
2020-11-03 17:47:06 +01:00
if ( action instanceof ExtensionDropDownAction ) {
return action . createActionViewItem ( ) ;
}
2020-10-22 00:20:25 +02:00
if ( action instanceof ActionWithDropDownAction ) {
2020-10-22 15:09:22 +02:00
return new ExtensionActionWithDropdownActionViewItem ( action , { icon : true , label : true , menuActionsOrProvider : { getActions : ( ) = > action . menuActions } , menuActionClassNames : ( action . class || '' ) . split ( ' ' ) } , this . contextMenuService ) ;
2016-10-19 13:04:42 +02:00
}
2019-03-21 01:13:31 +01:00
return undefined ;
2021-04-07 18:31:09 +02:00
} ,
focusOnlyEnabledItems : true
2019-08-14 01:09:36 +02:00
} ) ) ;
2018-06-19 20:53:23 +02:00
2021-07-22 23:31:04 +02:00
const status = append ( actionsAndStatusContainer , $ ( '.status' ) ) ;
2021-11-26 20:09:01 +01:00
const preReleaseText = append ( details , $ ( '.pre-release-text' ) ) ;
2021-07-22 15:32:18 +02:00
const recommendation = append ( details , $ ( '.recommendation' ) ) ;
2017-10-25 06:34:34 +02:00
2019-08-14 01:09:36 +02:00
this . _register ( Event . chain ( extensionActionBar . onDidRun )
2016-09-12 15:33:14 +02:00
. map ( ( { error } ) = > error )
. filter ( error = > ! ! error )
2019-06-12 20:49:11 +02:00
. on ( this . onError , this ) ) ;
2016-08-31 11:28:47 +02:00
2016-08-22 16:18:32 +02:00
const body = append ( root , $ ( '.body' ) ) ;
2019-08-14 01:09:36 +02:00
const navbar = new NavBar ( body ) ;
const content = append ( body , $ ( '.content' ) ) ;
2020-11-12 03:06:17 +01:00
content . id = generateUuid ( ) ; // An id is needed for the webview parent flow to
2019-08-14 01:09:36 +02:00
this . template = {
builtin ,
content ,
description ,
header ,
icon ,
iconContainer ,
2020-04-04 11:42:48 +02:00
version ,
2021-11-23 00:32:43 +01:00
preRelease ,
2019-08-14 01:09:36 +02:00
installCount ,
name ,
navbar ,
preview ,
publisher ,
2021-10-24 12:25:08 +02:00
publisherDisplayName ,
verifiedPublisherIcon ,
2019-08-14 01:09:36 +02:00
rating ,
2021-07-22 23:31:04 +02:00
actionsAndStatusContainer ,
2021-07-22 15:32:18 +02:00
extensionActionBar ,
2021-11-26 20:02:54 +01:00
preReleaseText ,
2021-07-22 15:32:18 +02:00
status ,
recommendation
2019-08-14 01:09:36 +02:00
} ;
2016-04-19 09:37:45 +02:00
}
2019-07-29 17:36:32 +02:00
private onClick ( element : HTMLElement , callback : ( ) = > void ) : IDisposable {
const disposables : DisposableStore = new DisposableStore ( ) ;
disposables . add ( addDisposableListener ( element , EventType . CLICK , finalHandler ( callback ) ) ) ;
disposables . add ( addDisposableListener ( element , EventType . KEY_UP , e = > {
const keyboardEvent = new StandardKeyboardEvent ( e ) ;
if ( keyboardEvent . equals ( KeyCode . Space ) || keyboardEvent . equals ( KeyCode . Enter ) ) {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
callback ( ) ;
}
} ) ) ;
return disposables ;
}
2021-11-23 00:32:43 +01:00
override async setInput ( input : ExtensionsInput , options : IExtensionEditorOptions | undefined , context : IEditorOpenContext , token : CancellationToken ) : Promise < void > {
2020-08-24 07:57:59 +02:00
await super . setInput ( input , options , context , token ) ;
2021-11-23 00:32:43 +01:00
this . updatePreReleaseVersionContext ( ) ;
2019-08-14 01:09:36 +02:00
if ( this . template ) {
2021-11-23 00:32:43 +01:00
await this . updateTemplate ( input . extension , this . template , ! ! options ? . preserveFocus ) ;
}
}
override setOptions ( options : IExtensionEditorOptions | undefined ) : void {
const currentOptions : IExtensionEditorOptions | undefined = this . options ;
super . setOptions ( options ) ;
this . updatePreReleaseVersionContext ( ) ;
if ( currentOptions ? . showPreReleaseVersion !== options ? . showPreReleaseVersion ) {
this . openTab ( ExtensionEditorTab . Readme ) ;
}
}
private updatePreReleaseVersionContext ( ) : void {
let showPreReleaseVersion = ( < IExtensionEditorOptions | undefined > this . options ) ? . showPreReleaseVersion ;
if ( isUndefined ( showPreReleaseVersion ) ) {
showPreReleaseVersion = ! ! ( < ExtensionsInput > this . input ) . extension . gallery ? . properties . isPreReleaseVersion ;
2019-08-14 01:09:36 +02:00
}
2021-11-23 00:32:43 +01:00
this . showPreReleaseVersionContextKey ? . set ( showPreReleaseVersion ) ;
2019-08-14 01:09:36 +02:00
}
2021-07-14 11:44:56 +02:00
async openTab ( tab : ExtensionEditorTab ) : Promise < void > {
2021-10-06 18:17:37 +02:00
if ( ! this . input || ! this . template ) {
return ;
}
if ( this . template . navbar . switch ( tab ) ) {
return ;
}
// Fallback to Readme tab if ExtensionPack tab does not exist
if ( tab === ExtensionEditorTab . ExtensionPack ) {
this . template . navbar . switch ( ExtensionEditorTab . Readme ) ;
2021-07-14 11:44:56 +02:00
}
}
2021-11-23 00:32:43 +01:00
private async updateTemplate ( extension : IExtension , template : IExtensionEditorTemplate , preserveFocus : boolean ) : Promise < void > {
2019-02-12 10:13:07 +01:00
this . activeElement = null ;
this . editorLoadComplete = false ;
2020-12-18 22:56:42 +01:00
if ( this . currentIdentifier !== extension . identifier . id ) {
this . initialScrollProgress . clear ( ) ;
this . currentIdentifier = extension . identifier . id ;
}
2019-06-12 20:49:11 +02:00
this . transientDisposables . clear ( ) ;
2019-02-12 10:13:07 +01:00
2021-11-23 00:32:43 +01:00
this . extensionReadme = new Cache ( ( ) = > createCancelablePromise ( token = > extension . getReadme ( ! ! this . showPreReleaseVersionContextKey ? . get ( ) , token ) ) ) ;
this . extensionChangelog = new Cache ( ( ) = > createCancelablePromise ( token = > extension . getChangelog ( ! ! this . showPreReleaseVersionContextKey ? . get ( ) , token ) ) ) ;
this . extensionManifest = new Cache ( ( ) = > createCancelablePromise ( token = > extension . getManifest ( ! ! this . showPreReleaseVersionContextKey ? . get ( ) , token ) ) ) ;
2019-02-12 10:13:07 +01:00
2019-08-14 01:09:36 +02:00
const remoteBadge = this . instantiationService . createInstance ( RemoteBadgeWidget , template . iconContainer , true ) ;
2021-06-09 16:36:59 +02:00
this . transientDisposables . add ( addDisposableListener ( template . icon , 'error' , ( ) = > template . icon . src = extension . iconUrlFallback , { once : true } ) ) ;
2019-08-14 01:09:36 +02:00
template . icon . src = extension . iconUrl ;
2019-02-12 10:13:07 +01:00
2019-08-14 01:09:36 +02:00
template . name . textContent = extension . displayName ;
2021-07-17 09:37:07 +02:00
template . version . textContent = ` v ${ extension . version } ` ;
2019-08-14 01:09:36 +02:00
template . preview . style . display = extension . preview ? 'inherit' : 'none' ;
2020-10-09 16:38:27 +02:00
template . builtin . style . display = extension . isBuiltin ? 'inherit' : 'none' ;
2019-02-12 10:13:07 +01:00
2019-08-14 01:09:36 +02:00
template . description . textContent = extension . description ;
2019-02-12 10:13:07 +01:00
2020-04-07 13:10:33 +02:00
const extRecommendations = this . extensionRecommendationsService . getAllRecommendationsWithReason ( ) ;
2019-02-12 10:13:07 +01:00
let recommendationsData = { } ;
if ( extRecommendations [ extension . identifier . id . toLowerCase ( ) ] ) {
recommendationsData = { recommendationReason : extRecommendations [ extension . identifier . id . toLowerCase ( ) ] . reasonId } ;
}
/ * _ _ G D P R _ _
"extensionGallery:openExtension" : {
"recommendationReason" : { "classification" : "SystemMetaData" , "purpose" : "FeatureInsight" , "isMeasurement" : true } ,
"${include}" : [
"${GalleryExtensionTelemetryData}"
]
}
* /
2020-09-08 10:07:21 +02:00
this . telemetryService . publicLog ( 'extensionGallery:openExtension' , { . . . extension . telemetryData , . . . recommendationsData } ) ;
2019-02-12 10:13:07 +01:00
2020-09-08 11:01:43 +02:00
template . name . classList . toggle ( 'clickable' , ! ! extension . url ) ;
2021-07-01 16:44:36 +02:00
// subtitle
2020-09-08 11:01:43 +02:00
template . publisher . classList . toggle ( 'clickable' , ! ! extension . url ) ;
2021-10-24 12:25:08 +02:00
template . publisherDisplayName . textContent = extension . publisherDisplayName ;
template . verifiedPublisherIcon . style . display = extension . publisherDomain ? . verified ? 'inherit' : 'none' ;
2021-10-25 17:58:54 +02:00
template . publisher . title = extension . publisherDomain ? . link ? localize ( 'publisher verified tooltip' , "This publisher has verified ownership of {0}" , URI . parse ( extension . publisherDomain . link ) . authority ) : '' ;
2021-07-01 16:44:36 +02:00
template . installCount . parentElement ? . classList . toggle ( 'hide' , ! extension . url ) ;
template . rating . parentElement ? . classList . toggle ( 'hide' , ! extension . url ) ;
2020-09-08 11:01:43 +02:00
template . rating . classList . toggle ( 'clickable' , ! ! extension . url ) ;
2021-07-01 16:44:36 +02:00
2019-02-12 10:13:07 +01:00
if ( extension . url ) {
2019-08-20 11:28:55 +02:00
this . transientDisposables . add ( this . onClick ( template . name , ( ) = > this . openerService . open ( URI . parse ( extension . url ! ) ) ) ) ;
2021-07-14 15:28:38 +02:00
this . transientDisposables . add ( this . onClick ( template . rating , ( ) = > this . openerService . open ( URI . parse ( ` ${ extension . url } &ssr=false#review-details ` ) ) ) ) ;
2019-08-14 01:09:36 +02:00
this . transientDisposables . add ( this . onClick ( template . publisher , ( ) = > {
2021-09-21 18:44:43 +02:00
this . paneCompositeService . openPaneComposite ( VIEWLET_ID , ViewContainerLocation . Sidebar , true )
2019-12-10 00:30:46 +01:00
. then ( viewlet = > viewlet ? . getViewPaneContainer ( ) as IExtensionsViewPaneContainer )
2019-02-12 10:13:07 +01:00
. then ( viewlet = > viewlet . search ( ` publisher:" ${ extension . publisherDisplayName } " ` ) ) ;
2019-07-29 17:36:32 +02:00
} ) ) ;
2019-02-12 10:13:07 +01:00
}
2016-06-21 13:57:51 +02:00
2019-02-12 10:13:07 +01:00
const widgets = [
2021-11-23 21:02:46 +01:00
this . instantiationService . createInstance ( PreReleaseIndicatorWidget , template . preRelease , { label : true , icon : false } ) ,
2019-02-12 10:13:07 +01:00
remoteBadge ,
2019-08-14 01:09:36 +02:00
this . instantiationService . createInstance ( InstallCountWidget , template . installCount , false ) ,
this . instantiationService . createInstance ( RatingsWidget , template . rating , false )
2019-02-12 10:13:07 +01:00
] ;
const reloadAction = this . instantiationService . createInstance ( ReloadAction ) ;
2020-10-22 15:09:22 +02:00
const combinedInstallAction = this . instantiationService . createInstance ( InstallDropdownAction ) ;
2019-02-12 10:13:07 +01:00
const actions = [
reloadAction ,
2021-07-05 15:58:05 +02:00
this . instantiationService . createInstance ( ExtensionStatusLabelAction ) ,
2019-02-12 10:13:07 +01:00
this . instantiationService . createInstance ( UpdateAction ) ,
2020-03-11 08:06:28 +01:00
this . instantiationService . createInstance ( SetColorThemeAction , await this . workbenchThemeService . getColorThemes ( ) ) ,
this . instantiationService . createInstance ( SetFileIconThemeAction , await this . workbenchThemeService . getFileIconThemes ( ) ) ,
this . instantiationService . createInstance ( SetProductIconThemeAction , await this . workbenchThemeService . getProductIconThemes ( ) ) ,
2019-02-12 10:13:07 +01:00
this . instantiationService . createInstance ( EnableDropDownAction ) ,
2020-11-13 09:41:07 +01:00
this . instantiationService . createInstance ( DisableDropDownAction ) ,
2020-05-05 13:19:12 +02:00
this . instantiationService . createInstance ( RemoteInstallAction , false ) ,
2019-04-23 17:09:40 +02:00
this . instantiationService . createInstance ( LocalInstallAction ) ,
2020-12-02 14:29:21 +01:00
this . instantiationService . createInstance ( WebInstallAction ) ,
2019-04-09 19:24:20 +02:00
combinedInstallAction ,
2020-10-22 15:09:22 +02:00
this . instantiationService . createInstance ( InstallingLabelAction ) ,
2020-11-09 16:18:23 +01:00
this . instantiationService . createInstance ( ActionWithDropDownAction , 'extensions.uninstall' , UninstallAction . UninstallLabel , [
2021-11-23 00:32:43 +01:00
[
this . instantiationService . createInstance ( UninstallAction ) ,
this . instantiationService . createInstance ( InstallAnotherVersionAction ) ,
]
2020-11-09 16:18:23 +01:00
] ) ,
2021-11-23 22:59:10 +01:00
this . instantiationService . createInstance ( SwitchToPreReleaseVersionAction ) ,
this . instantiationService . createInstance ( SwitchToReleasedVersionAction ) ,
2021-11-26 02:02:41 +01:00
this . instantiationService . createInstance ( SwitchUnsupportedExtensionToPreReleaseExtensionAction ) ,
2020-11-09 16:18:23 +01:00
this . instantiationService . createInstance ( ToggleSyncExtensionAction ) ,
2021-11-23 00:32:43 +01:00
new ExtensionEditorManageExtensionAction ( this . scopedContextKeyService || this . contextKeyService , this . instantiationService ) ,
2019-02-12 10:13:07 +01:00
] ;
2021-07-22 15:32:18 +02:00
const extensionStatus = this . instantiationService . createInstance ( ExtensionStatusAction ) ;
const extensionContainers : ExtensionContainers = this . instantiationService . createInstance ( ExtensionContainers , [ . . . actions , . . . widgets , extensionStatus ] ) ;
2019-02-12 10:13:07 +01:00
extensionContainers . extension = extension ;
2019-08-14 01:09:36 +02:00
template . extensionActionBar . clear ( ) ;
template . extensionActionBar . push ( actions , { icon : true , label : true } ) ;
2021-04-07 18:31:09 +02:00
template . extensionActionBar . setFocusable ( true ) ;
2021-09-23 21:42:27 +02:00
// update focusable elements when the enablement of an action changes
this . transientDisposables . add ( Event . any ( . . . actions . map ( a = > Event . filter ( a . onDidChange , e = > e . enabled !== undefined ) ) ) ( ( ) = > {
template . extensionActionBar . setFocusable ( false ) ;
template . extensionActionBar . setFocusable ( true ) ;
} ) ) ;
2019-06-12 20:49:11 +02:00
for ( const disposable of [ . . . actions , . . . widgets , extensionContainers ] ) {
this . transientDisposables . add ( disposable ) ;
}
2019-02-12 10:13:07 +01:00
2021-11-26 20:02:54 +01:00
this . setPreReleaseText ( extension , template ) ;
2021-07-22 23:31:04 +02:00
this . setStatus ( extension , extensionStatus , template ) ;
2021-07-22 15:32:18 +02:00
this . setRecommendationText ( extension , template ) ;
2020-08-03 19:19:53 +02:00
template . content . innerText = '' ; // Clear content before setting navbar actions.
2019-02-12 10:13:07 +01:00
2019-08-14 01:09:36 +02:00
template . navbar . clear ( ) ;
2019-02-12 10:13:07 +01:00
if ( extension . hasReadme ( ) ) {
2021-07-14 11:44:56 +02:00
template . navbar . push ( ExtensionEditorTab . Readme , localize ( 'details' , "Details" ) , localize ( 'detailstooltip' , "Extension details, rendered from the extension's 'README.md' file" ) ) ;
2019-02-12 10:13:07 +01:00
}
2020-03-04 09:26:14 +01:00
const manifest = await this . extensionManifest . get ( ) . promise ;
if ( manifest ) {
combinedInstallAction . manifest = manifest ;
}
if ( manifest && manifest . contributes ) {
2021-07-14 11:44:56 +02:00
template . navbar . push ( ExtensionEditorTab . Contributions , localize ( 'contributions' , "Feature Contributions" ) , localize ( 'contributionstooltip' , "Lists contributions to VS Code by this extension" ) ) ;
2020-03-04 09:26:14 +01:00
}
if ( extension . hasChangelog ( ) ) {
2021-07-14 11:44:56 +02:00
template . navbar . push ( ExtensionEditorTab . Changelog , localize ( 'changelog' , "Changelog" ) , localize ( 'changelogtooltip' , "Extension update history, rendered from the extension's 'CHANGELOG.md' file" ) ) ;
2020-03-04 09:26:14 +01:00
}
if ( extension . dependencies . length ) {
2021-07-14 11:44:56 +02:00
template . navbar . push ( ExtensionEditorTab . Dependencies , localize ( 'dependencies' , "Dependencies" ) , localize ( 'dependenciestooltip' , "Lists extensions this extension depends on" ) ) ;
2020-03-04 09:26:14 +01:00
}
2021-03-25 09:03:07 +01:00
if ( manifest && manifest . extensionPack ? . length && ! this . shallRenderAsExensionPack ( manifest ) ) {
2021-07-14 11:44:56 +02:00
template . navbar . push ( ExtensionEditorTab . ExtensionPack , localize ( 'extensionpack' , "Extension Pack" ) , localize ( 'extensionpacktooltip' , "Lists extensions those will be installed together with this extension" ) ) ;
2021-03-25 09:03:07 +01:00
}
2020-03-04 09:26:14 +01:00
2021-07-14 14:18:32 +02:00
const addRuntimeStatusSection = ( ) = > template . navbar . push ( ExtensionEditorTab . RuntimeStatus , localize ( 'runtimeStatus' , "Runtime Status" ) , localize ( 'runtimeStatus description' , "Extension runtime status" ) ) ;
if ( this . extensionsWorkbenchService . getExtensionStatus ( extension ) ) {
addRuntimeStatusSection ( ) ;
} else {
const disposable = this . extensionService . onDidChangeExtensionsStatus ( e = > {
if ( e . some ( extensionIdentifier = > areSameExtensions ( { id : extensionIdentifier.value } , extension . identifier ) ) ) {
addRuntimeStatusSection ( ) ;
disposable . dispose ( ) ;
}
} , this , this . transientDisposables ) ;
}
2020-03-04 09:26:14 +01:00
if ( template . navbar . currentId ) {
this . onNavbarChange ( extension , { id : template.navbar.currentId , focus : ! preserveFocus } , template ) ;
}
template . navbar . onChange ( e = > this . onNavbarChange ( extension , e , template ) , this , this . transientDisposables ) ;
this . editorLoadComplete = true ;
2016-11-01 11:35:43 +01:00
}
2017-07-07 22:20:53 +02:00
2021-11-26 20:02:54 +01:00
private setPreReleaseText ( extension : IExtension , template : IExtensionEditorTemplate ) : void {
let preReleaseText : string | undefined ;
reset ( template . preReleaseText ) ;
const disposables = this . transientDisposables . add ( new DisposableStore ( ) ) ;
const updatePreReleaseText = ( ) = > {
const newPreReleaseText = ExtensionHoverWidget . getPreReleaseMessage ( extension ) ;
if ( preReleaseText !== newPreReleaseText ) {
preReleaseText = newPreReleaseText ;
disposables . clear ( ) ;
reset ( template . preReleaseText ) ;
if ( preReleaseText ) {
append ( template . preReleaseText , $ ( ` span ${ ThemeIcon . asCSSSelector ( preReleaseIcon ) } ` ) ) ;
disposables . add ( this . renderMarkdownText ( preReleaseText , template . preReleaseText ) ) ;
}
}
} ;
updatePreReleaseText ( ) ;
this . transientDisposables . add ( this . extensionsWorkbenchService . onChange ( e = > {
if ( e && areSameExtensions ( e . identifier , extension . identifier ) ) {
updatePreReleaseText ( ) ;
}
} ) ) ;
}
2021-07-22 23:31:04 +02:00
private setStatus ( extension : IExtension , extensionStatus : ExtensionStatusAction , template : IExtensionEditorTemplate ) : void {
const disposables = new DisposableStore ( ) ;
this . transientDisposables . add ( disposables ) ;
2021-07-22 15:32:18 +02:00
const updateStatus = ( ) = > {
2021-07-22 23:31:04 +02:00
disposables . clear ( ) ;
2021-07-22 15:32:18 +02:00
reset ( template . status ) ;
const status = extensionStatus . status ;
if ( status ) {
if ( status . icon ) {
2021-07-22 23:31:04 +02:00
const statusIconActionBar = disposables . add ( new ActionBar ( template . status , { animated : false } ) ) ;
statusIconActionBar . push ( extensionStatus , { icon : true , label : false } ) ;
2021-07-22 15:32:18 +02:00
}
2021-11-26 20:02:54 +01:00
disposables . add ( this . renderMarkdownText ( status . message . value , append ( template . status , $ ( '.status-text' ) ) ) ) ;
2021-07-22 15:32:18 +02:00
}
} ;
updateStatus ( ) ;
this . transientDisposables . add ( extensionStatus . onDidChangeStatus ( ( ) = > updateStatus ( ) ) ) ;
2021-07-22 23:31:04 +02:00
const updateActionLayout = ( ) = > template . actionsAndStatusContainer . classList . toggle ( 'list-layout' , extension . state === ExtensionState . Installed ) ;
updateActionLayout ( ) ;
this . transientDisposables . add ( this . extensionsWorkbenchService . onChange ( ( ) = > updateActionLayout ( ) ) ) ;
2021-07-22 15:32:18 +02:00
}
2018-11-15 06:01:11 +01:00
2021-07-22 15:32:18 +02:00
private setRecommendationText ( extension : IExtension , template : IExtensionEditorTemplate ) : void {
const updateRecommendationText = ( ) = > {
reset ( template . recommendation ) ;
2020-09-27 12:09:32 +02:00
const extRecommendations = this . extensionRecommendationsService . getAllRecommendationsWithReason ( ) ;
if ( extRecommendations [ extension . identifier . id . toLowerCase ( ) ] ) {
2021-07-26 17:11:59 +02:00
const reasonText = extRecommendations [ extension . identifier . id . toLowerCase ( ) ] . reasonText ;
if ( reasonText ) {
append ( template . recommendation , $ ( ` div ${ ThemeIcon . asCSSSelector ( starEmptyIcon ) } ` ) ) ;
append ( template . recommendation , $ ( ` div.recommendation-text ` , undefined , reasonText ) ) ;
}
2020-09-27 12:09:32 +02:00
} else if ( this . extensionIgnoredRecommendationsService . globalIgnoredRecommendations . indexOf ( extension . identifier . id . toLowerCase ( ) ) !== - 1 ) {
2021-07-22 23:50:20 +02:00
append ( template . recommendation , $ ( ` div.recommendation-text ` , undefined , localize ( 'recommendationHasBeenIgnored' , "You have chosen not to receive recommendations for this extension." ) ) ) ;
2018-11-15 06:01:11 +01:00
}
2020-09-27 12:09:32 +02:00
} ;
2021-07-22 15:32:18 +02:00
updateRecommendationText ( ) ;
this . transientDisposables . add ( this . extensionRecommendationsService . onDidChangeRecommendations ( ( ) = > updateRecommendationText ( ) ) ) ;
2018-11-15 06:01:11 +01:00
}
2021-11-26 20:02:54 +01:00
private renderMarkdownText ( markdownText : string , parent : HTMLElement ) : IDisposable {
const disposables = new DisposableStore ( ) ;
const rendered = disposables . add ( renderMarkdown ( new MarkdownString ( markdownText , { isTrusted : true , supportThemeIcons : true } ) , {
actionHandler : {
callback : ( content ) = > {
this . openerService . open ( content , { allowCommands : true } ) . catch ( onUnexpectedError ) ;
} ,
disposables : disposables
}
} ) ) ;
append ( parent , rendered . element ) ;
return disposables ;
}
2021-04-08 19:34:28 +02:00
override clearInput ( ) : void {
2019-07-16 20:32:32 +02:00
this . contentDisposables . clear ( ) ;
this . transientDisposables . clear ( ) ;
super . clearInput ( ) ;
}
2021-04-08 19:34:28 +02:00
override focus ( ) : void {
2020-07-13 20:57:26 +02:00
this . activeElement ? . focus ( ) ;
2018-09-12 16:20:43 +02:00
}
2017-08-28 15:24:32 +02:00
showFind ( ) : void {
2020-07-13 20:57:26 +02:00
this . activeWebview ? . showFind ( ) ;
2017-08-28 15:24:32 +02:00
}
2019-08-21 20:47:34 +02:00
runFindAction ( previous : boolean ) : void {
2020-07-13 20:57:26 +02:00
this . activeWebview ? . runFindAction ( previous ) ;
}
2021-11-05 01:29:42 +01:00
public get activeWebview ( ) : IWebview | undefined {
if ( ! this . activeElement || ! ( this . activeElement as IWebview ) . runFindAction ) {
2020-07-13 20:57:26 +02:00
return undefined ;
2019-08-21 20:47:34 +02:00
}
2021-11-05 01:29:42 +01:00
return this . activeElement as IWebview ;
2019-08-21 20:47:34 +02:00
}
2019-08-14 01:09:36 +02:00
private onNavbarChange ( extension : IExtension , { id , focus } : { id : string | null , focus : boolean } , template : IExtensionEditorTemplate ) : void {
2018-04-29 04:24:58 +02:00
if ( this . editorLoadComplete ) {
/ * _ _ G D P R _ _
"extensionEditor:navbarChange" : {
2018-04-29 08:47:38 +02:00
"navItem" : { "classification" : "SystemMetaData" , "purpose" : "FeatureInsight" } ,
2018-04-29 04:24:58 +02:00
"${include}" : [
"${GalleryExtensionTelemetryData}"
]
}
* /
2020-09-08 10:07:21 +02:00
this . telemetryService . publicLog ( 'extensionEditor:navbarChange' , { . . . extension . telemetryData , navItem : id } ) ;
2018-04-29 04:24:58 +02:00
}
2019-06-12 20:49:11 +02:00
this . contentDisposables . clear ( ) ;
2020-08-03 19:19:53 +02:00
template . content . innerText = '' ;
2018-09-12 16:20:43 +02:00
this . activeElement = null ;
2019-08-14 01:09:36 +02:00
if ( id ) {
2020-11-11 08:17:27 +01:00
const cts = new CancellationTokenSource ( ) ;
this . contentDisposables . add ( toDisposable ( ( ) = > cts . dispose ( true ) ) ) ;
this . open ( id , extension , template , cts . token )
2019-08-14 01:09:36 +02:00
. then ( activeElement = > {
2020-11-11 08:17:27 +01:00
if ( cts . token . isCancellationRequested ) {
return ;
}
2019-08-14 01:09:36 +02:00
this . activeElement = activeElement ;
if ( focus ) {
this . focus ( ) ;
}
} ) ;
}
2018-09-12 16:20:43 +02:00
}
2020-11-11 08:17:27 +01:00
private open ( id : string , extension : IExtension , template : IExtensionEditorTemplate , token : CancellationToken ) : Promise < IActiveElement | null > {
2016-08-22 16:18:32 +02:00
switch ( id ) {
2021-07-15 00:01:13 +02:00
case ExtensionEditorTab.Readme : return this . openDetails ( extension , template , token ) ;
2021-07-14 11:44:56 +02:00
case ExtensionEditorTab.Contributions : return this . openContributions ( template , token ) ;
case ExtensionEditorTab.Changelog : return this . openChangelog ( template , token ) ;
case ExtensionEditorTab.Dependencies : return this . openExtensionDependencies ( extension , template , token ) ;
case ExtensionEditorTab.ExtensionPack : return this . openExtensionPack ( extension , template , token ) ;
2021-07-15 00:01:13 +02:00
case ExtensionEditorTab.RuntimeStatus : return this . openRuntimeStatus ( extension , template , token ) ;
2016-06-17 11:31:08 +02:00
}
2018-10-02 19:32:35 +02:00
return Promise . resolve ( null ) ;
2016-08-22 16:18:32 +02:00
}
2016-04-19 10:07:48 +02:00
2021-07-15 00:01:13 +02:00
private async openMarkdown ( cacheResult : CacheResult < string > , noContentCopy : string , container : HTMLElement , webviewIndex : WebviewIndex , token : CancellationToken ) : Promise < IActiveElement | null > {
2019-09-20 01:54:50 +02:00
try {
2021-07-15 00:01:13 +02:00
const body = await this . renderMarkdown ( cacheResult , container ) ;
2020-11-11 08:17:27 +01:00
if ( token . isCancellationRequested ) {
return Promise . resolve ( null ) ;
}
2019-09-20 01:54:50 +02:00
2020-03-04 08:56:21 +01:00
const webview = this . contentDisposables . add ( this . webviewService . createWebviewOverlay ( 'extensionEditor' , {
2019-09-20 01:54:50 +02:00
enableFindWidget : true ,
2020-12-18 22:56:42 +01:00
tryRestoreScrollPosition : true ,
2020-05-15 20:34:08 +02:00
} , { } , undefined ) ) ;
2019-09-20 01:54:50 +02:00
2020-12-18 22:56:42 +01:00
webview . initialScrollProgress = this . initialScrollProgress . get ( webviewIndex ) || 0 ;
2020-11-11 02:49:08 +01:00
webview . claim ( this , this . scopedContextKeyService ) ;
2021-07-15 00:01:13 +02:00
setParentFlowTo ( webview . container , container ) ;
webview . layoutWebviewOverElement ( container ) ;
2020-11-12 03:06:17 +01:00
2020-03-04 08:56:21 +01:00
webview . html = body ;
2020-12-19 00:21:03 +01:00
webview . claim ( this , undefined ) ;
2019-09-20 01:54:50 +02:00
2020-03-04 08:56:21 +01:00
this . contentDisposables . add ( webview . onDidFocus ( ( ) = > this . fireOnDidFocus ( ) ) ) ;
2020-12-18 22:56:42 +01:00
this . contentDisposables . add ( webview . onDidScroll ( ( ) = > this . initialScrollProgress . set ( webviewIndex , webview . initialScrollProgress ) ) ) ;
2019-09-20 01:54:50 +02:00
const removeLayoutParticipant = arrays . insert ( this . layoutParticipants , {
layout : ( ) = > {
2021-07-15 00:01:13 +02:00
webview . layoutWebviewOverElement ( container ) ;
2019-09-20 01:54:50 +02:00
}
2018-08-10 15:10:54 +02:00
} ) ;
2019-09-20 01:54:50 +02:00
this . contentDisposables . add ( toDisposable ( removeLayoutParticipant ) ) ;
2019-09-20 02:02:04 +02:00
let isDisposed = false ;
this . contentDisposables . add ( toDisposable ( ( ) = > { isDisposed = true ; } ) ) ;
2020-03-02 22:18:51 +01:00
this . contentDisposables . add ( this . themeService . onDidColorThemeChange ( async ( ) = > {
2019-09-20 02:02:04 +02:00
// Render again since syntax highlighting of code blocks may have changed
2021-07-15 00:01:13 +02:00
const body = await this . renderMarkdown ( cacheResult , container ) ;
2019-09-20 02:02:04 +02:00
if ( ! isDisposed ) { // Make sure we weren't disposed of in the meantime
2020-03-04 08:56:21 +01:00
webview . html = body ;
2019-09-20 02:02:04 +02:00
}
} ) ) ;
2020-03-04 08:56:21 +01:00
this . contentDisposables . add ( webview . onDidClickLink ( link = > {
2019-09-20 01:54:50 +02:00
if ( ! link ) {
return ;
}
2020-06-12 04:58:01 +02:00
// Only allow links with specific schemes
2021-04-13 19:50:56 +02:00
if ( matchesScheme ( link , Schemas . http ) || matchesScheme ( link , Schemas . https ) || matchesScheme ( link , Schemas . mailto ) ) {
2019-09-20 01:54:50 +02:00
this . openerService . open ( link ) ;
}
2021-04-13 19:50:56 +02:00
if ( matchesScheme ( link , Schemas . command ) && URI . parse ( link ) . path === ShowCurrentReleaseNotesActionId ) {
this . openerService . open ( link , { allowCommands : true } ) ; // TODO@sandy081 use commands service
}
2021-08-23 23:47:46 +02:00
} ) ) ;
2019-09-20 01:54:50 +02:00
2020-03-04 08:56:21 +01:00
return webview ;
2019-09-20 01:54:50 +02:00
} catch ( e ) {
2021-07-15 00:01:13 +02:00
const p = append ( container , $ ( 'p.nocontent' ) ) ;
2019-09-20 01:54:50 +02:00
p . textContent = noContentCopy ;
return p ;
}
2016-08-22 16:18:32 +02:00
}
2016-04-19 10:07:48 +02:00
2021-07-15 00:01:13 +02:00
private async renderMarkdown ( cacheResult : CacheResult < string > , container : HTMLElement ) {
const contents = await this . loadContents ( ( ) = > cacheResult , container ) ;
2019-09-20 01:56:35 +02:00
const content = await renderMarkdownDocument ( contents , this . extensionService , this . modeService ) ;
2021-04-24 00:01:54 +02:00
return this . renderBody ( content ) ;
2019-09-20 01:56:35 +02:00
}
2019-08-13 00:32:21 +02:00
private async renderBody ( body : string ) : Promise < string > {
const nonce = generateUuid ( ) ;
2019-09-20 01:51:11 +02:00
const colorMap = TokenizationRegistry . getColorMap ( ) ;
const css = colorMap ? generateTokensCSSForColorMap ( colorMap ) : '' ;
2019-08-13 00:08:34 +02:00
return ` <!DOCTYPE html>
< html >
< head >
< meta http - equiv = "Content-type" content = "text/html;charset=UTF-8" >
2019-08-13 00:32:21 +02:00
< meta http - equiv = "Content-Security-Policy" content = "default-src 'none'; img-src https: data:; media-src https:; script-src 'none'; style-src 'nonce-${nonce}';" >
< style nonce = "${nonce}" >
2021-04-24 00:01:54 +02:00
$ { DEFAULT_MARKDOWN_STYLES }
2019-09-20 01:51:11 +02:00
2019-08-13 00:32:21 +02:00
# scroll - to - top {
position : fixed ;
width : 40px ;
height : 40px ;
right : 25px ;
bottom : 25px ;
background - color : # 444444 ;
border - radius : 50 % ;
cursor : pointer ;
box - shadow : 1px 1 px 1 px rgba ( 0 , 0 , 0 , . 25 ) ;
outline : none ;
display : flex ;
justify - content : center ;
align - items : center ;
}
# scroll - to - top :hover {
background - color : # 007 acc ;
box - shadow : 2px 2 px 2 px rgba ( 0 , 0 , 0 , . 25 ) ;
}
body . vscode - light # scroll - to - top {
background - color : # 949494 ;
}
body . vscode - high - contrast # scroll - to - top :hover {
background - color : # 007 acc ;
}
body . vscode - high - contrast # scroll - to - top {
background - color : black ;
border : 2px solid # 6 fc3df ;
box - shadow : none ;
}
body . vscode - high - contrast # scroll - to - top :hover {
background - color : # 007 acc ;
}
# scroll - to - top span . icon : : before {
content : "" ;
display : block ;
/* Chevron up icon */
background :url ( 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE5LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxNiAxNiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMTYgMTY7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbDojRkZGRkZGO30KCS5zdDF7ZmlsbDpub25lO30KPC9zdHlsZT4KPHRpdGxlPnVwY2hldnJvbjwvdGl0bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik04LDUuMWwtNy4zLDcuM0wwLDExLjZsOC04bDgsOGwtMC43LDAuN0w4LDUuMXoiLz4KPHJlY3QgY2xhc3M9InN0MSIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2Ii8+Cjwvc3ZnPgo=' ) ;
width : 16px ;
height : 16px ;
}
2019-09-20 01:51:11 +02:00
$ { css }
2019-08-13 00:32:21 +02:00
< / style >
2019-08-13 00:08:34 +02:00
< / head >
< body >
< a id = "scroll-to-top" role = "button" aria - label = "scroll to top" href = "#" > < span class = "icon" > < / span > < / a >
$ { body }
< / body >
< / html > ` ;
}
2021-07-15 00:01:13 +02:00
private async openDetails ( extension : IExtension , template : IExtensionEditorTemplate , token : CancellationToken ) : Promise < IActiveElement | null > {
2021-07-14 22:24:14 +02:00
const details = append ( template . content , $ ( '.details' ) ) ;
const readmeContainer = append ( details , $ ( '.readme-container' ) ) ;
const additionalDetailsContainer = append ( details , $ ( '.additional-details-container' ) ) ;
2021-07-15 00:01:13 +02:00
2021-07-26 11:23:08 +02:00
const layout = ( ) = > details . classList . toggle ( 'narrow' , this . dimension && this . dimension . width < 500 ) ;
layout ( ) ;
this . contentDisposables . add ( toDisposable ( arrays . insert ( this . layoutParticipants , { layout } ) ) ) ;
2021-07-15 00:01:13 +02:00
let activeElement : IActiveElement | null = null ;
const manifest = await this . extensionManifest ! . get ( ) . promise ;
if ( manifest && manifest . extensionPack ? . length && this . shallRenderAsExensionPack ( manifest ) ) {
activeElement = await this . openExtensionPackReadme ( manifest , readmeContainer , token ) ;
} else {
activeElement = await this . openMarkdown ( this . extensionReadme ! . get ( ) , localize ( 'noReadme' , "No README available." ) , readmeContainer , WebviewIndex . Readme , token ) ;
}
2021-07-14 22:24:14 +02:00
this . renderAdditionalDetails ( additionalDetailsContainer , extension ) ;
return activeElement ;
}
2021-07-15 00:01:13 +02:00
private shallRenderAsExensionPack ( manifest : IExtensionManifest ) : boolean {
return ! ! ( manifest . categories ? . some ( category = > category . toLowerCase ( ) === 'extension packs' ) ) ;
}
private async openExtensionPackReadme ( manifest : IExtensionManifest , container : HTMLElement , token : CancellationToken ) : Promise < IActiveElement | null > {
if ( token . isCancellationRequested ) {
return Promise . resolve ( null ) ;
}
const extensionPackReadme = append ( container , $ ( 'div' , { class : 'extension-pack-readme' } ) ) ;
extensionPackReadme . style . margin = '0 auto' ;
extensionPackReadme . style . maxWidth = '882px' ;
const extensionPack = append ( extensionPackReadme , $ ( 'div' , { class : 'extension-pack' } ) ) ;
if ( manifest . extensionPack ! . length <= 3 ) {
extensionPackReadme . classList . add ( 'one-row' ) ;
} else if ( manifest . extensionPack ! . length <= 6 ) {
extensionPackReadme . classList . add ( 'two-rows' ) ;
} else if ( manifest . extensionPack ! . length <= 9 ) {
extensionPackReadme . classList . add ( 'three-rows' ) ;
} else {
extensionPackReadme . classList . add ( 'more-rows' ) ;
}
const extensionPackHeader = append ( extensionPack , $ ( 'div.header' ) ) ;
extensionPackHeader . textContent = localize ( 'extension pack' , "Extension Pack ({0})" , manifest . extensionPack ! . length ) ;
const extensionPackContent = append ( extensionPack , $ ( 'div' , { class : 'extension-pack-content' } ) ) ;
extensionPackContent . setAttribute ( 'tabindex' , '0' ) ;
append ( extensionPack , $ ( 'div.footer' ) ) ;
const readmeContent = append ( extensionPackReadme , $ ( 'div.readme-content' ) ) ;
await Promise . all ( [
this . renderExtensionPack ( manifest , extensionPackContent , token ) ,
this . openMarkdown ( this . extensionReadme ! . get ( ) , localize ( 'noReadme' , "No README available." ) , readmeContent , WebviewIndex . Readme , token ) ,
] ) ;
return { focus : ( ) = > extensionPackContent . focus ( ) } ;
}
2021-07-14 22:24:14 +02:00
private renderAdditionalDetails ( container : HTMLElement , extension : IExtension ) : void {
2021-07-15 01:51:59 +02:00
const content = $ ( 'div' , { class : 'additional-details-content' , tabindex : '0' } ) ;
const scrollableContent = new DomScrollableElement ( content , { } ) ;
const layout = ( ) = > scrollableContent . scanDomNode ( ) ;
const removeLayoutParticipant = arrays . insert ( this . layoutParticipants , { layout } ) ;
this . contentDisposables . add ( toDisposable ( removeLayoutParticipant ) ) ;
this . contentDisposables . add ( scrollableContent ) ;
this . renderCategories ( content , extension ) ;
this . renderResources ( content , extension ) ;
this . renderMoreInfo ( content , extension ) ;
append ( container , scrollableContent . getDomNode ( ) ) ;
scrollableContent . scanDomNode ( ) ;
2021-07-14 22:24:14 +02:00
}
private renderCategories ( container : HTMLElement , extension : IExtension ) : void {
if ( extension . categories . length ) {
2021-07-15 09:30:38 +02:00
const categoriesContainer = append ( container , $ ( '.categories-container' ) ) ;
append ( categoriesContainer , $ ( '.additional-details-title' , undefined , localize ( 'categories' , "Categories" ) ) ) ;
const categoriesElement = append ( categoriesContainer , $ ( '.categories' ) ) ;
2021-07-15 00:01:13 +02:00
for ( const category of extension . categories ) {
this . transientDisposables . add ( this . onClick ( append ( categoriesElement , $ ( 'span.category' , undefined , category ) ) , ( ) = > {
2021-09-21 18:44:43 +02:00
this . paneCompositeService . openPaneComposite ( VIEWLET_ID , ViewContainerLocation . Sidebar , true )
2021-07-15 00:01:13 +02:00
. then ( viewlet = > viewlet ? . getViewPaneContainer ( ) as IExtensionsViewPaneContainer )
. then ( viewlet = > viewlet . search ( ` @category:" ${ category } " ` ) ) ;
} ) ) ;
}
2021-07-14 22:24:14 +02:00
}
}
private renderResources ( container : HTMLElement , extension : IExtension ) : void {
const resources : [ string , URI ] [ ] = [ ] ;
if ( extension . url ) {
resources . push ( [ localize ( 'Marketplace' , "Marketplace" ) , URI . parse ( extension . url ) ] ) ;
}
if ( extension . repository ) {
resources . push ( [ localize ( 'repository' , "Repository" ) , URI . parse ( extension . repository ) ] ) ;
}
if ( extension . url && extension . licenseUrl ) {
resources . push ( [ localize ( 'license' , "License" ) , URI . parse ( extension . licenseUrl ) ] ) ;
}
2021-10-24 12:25:08 +02:00
if ( extension . publisherDomain ? . verified ) {
2021-10-25 17:58:54 +02:00
const publisherDomainUri = URI . parse ( extension . publisherDomain . link ) ;
resources . push ( [ publisherDomainUri . authority , publisherDomainUri ] ) ;
2021-10-24 12:25:08 +02:00
}
2021-07-14 22:24:14 +02:00
if ( resources . length ) {
2021-07-15 09:30:38 +02:00
const resourcesContainer = append ( container , $ ( '.resources-container' ) ) ;
append ( resourcesContainer , $ ( '.additional-details-title' , undefined , localize ( 'resources' , "Resources" ) ) ) ;
const resourcesElement = append ( resourcesContainer , $ ( '.resources' ) ) ;
2021-07-14 22:24:14 +02:00
for ( const [ label , uri ] of resources ) {
2021-10-24 12:25:08 +02:00
this . transientDisposables . add ( this . onClick ( append ( resourcesElement , $ ( 'a.resource' , { title : uri.toString ( ) } , label ) ) , ( ) = > this . openerService . open ( uri ) ) ) ;
2021-07-14 22:24:14 +02:00
}
}
}
2021-07-15 00:49:45 +02:00
private renderMoreInfo ( container : HTMLElement , extension : IExtension ) : void {
const gallery = extension . gallery ;
const moreInfoContainer = append ( container , $ ( '.more-info-container' ) ) ;
append ( moreInfoContainer , $ ( '.additional-details-title' , undefined , localize ( 'more info' , "More Info" ) ) ) ;
const moreInfo = append ( moreInfoContainer , $ ( '.more-info' ) ) ;
2021-07-17 09:37:07 +02:00
if ( gallery ) {
append ( moreInfo ,
$ ( '.more-info-entry' , undefined ,
2021-07-22 15:44:10 +02:00
$ ( 'div' , undefined , localize ( 'release date' , "Released on" ) ) ,
2021-07-27 23:25:48 +02:00
$ ( 'div' , undefined , new Date ( gallery . releaseDate ) . toLocaleString ( undefined , { hour12 : false } ) )
2021-07-17 09:37:07 +02:00
) ,
$ ( '.more-info-entry' , undefined ,
2021-07-22 15:44:10 +02:00
$ ( 'div' , undefined , localize ( 'last updated' , "Last updated" ) ) ,
2021-07-27 23:25:48 +02:00
$ ( 'div' , undefined , new Date ( gallery . lastUpdated ) . toLocaleString ( undefined , { hour12 : false } ) )
2021-07-17 09:37:07 +02:00
)
) ;
}
2021-07-15 00:49:45 +02:00
append ( moreInfo ,
$ ( '.more-info-entry' , undefined ,
2021-07-22 15:44:10 +02:00
$ ( 'div' , undefined , localize ( 'id' , "Identifier" ) ) ,
2021-07-17 09:37:07 +02:00
$ ( 'code' , undefined , extension . identifier . id )
) ) ;
2021-07-15 00:49:45 +02:00
}
2020-12-19 00:21:03 +01:00
private openChangelog ( template : IExtensionEditorTemplate , token : CancellationToken ) : Promise < IActiveElement | null > {
2021-07-15 00:01:13 +02:00
return this . openMarkdown ( this . extensionChangelog ! . get ( ) , localize ( 'noChangelog' , "No Changelog available." ) , template . content , WebviewIndex . Changelog , token ) ;
2016-09-14 09:45:37 +02:00
}
2020-11-11 08:17:27 +01:00
private openContributions ( template : IExtensionEditorTemplate , token : CancellationToken ) : Promise < IActiveElement | null > {
2021-07-28 14:57:50 +02:00
const content = $ ( 'div.subcontent.feature-contributions' , { tabindex : '0' } ) ;
2021-07-15 00:01:13 +02:00
return this . loadContents ( ( ) = > this . extensionManifest ! . get ( ) , template . content )
2016-08-23 15:30:58 +02:00
. then ( manifest = > {
2020-11-11 08:17:27 +01:00
if ( token . isCancellationRequested ) {
return null ;
}
2019-07-16 20:30:25 +02:00
if ( ! manifest ) {
return content ;
}
2016-08-24 16:07:48 +02:00
2019-07-16 20:30:25 +02:00
const scrollableContent = new DomScrollableElement ( content , { } ) ;
const layout = ( ) = > scrollableContent . scanDomNode ( ) ;
const removeLayoutParticipant = arrays . insert ( this . layoutParticipants , { layout } ) ;
this . contentDisposables . add ( toDisposable ( removeLayoutParticipant ) ) ;
const renders = [
this . renderSettings ( content , manifest , layout ) ,
this . renderCommands ( content , manifest , layout ) ,
2019-11-07 02:46:32 +01:00
this . renderCodeActions ( content , manifest , layout ) ,
2019-07-16 20:30:25 +02:00
this . renderLanguages ( content , manifest , layout ) ,
this . renderColorThemes ( content , manifest , layout ) ,
this . renderIconThemes ( content , manifest , layout ) ,
2021-03-22 17:59:35 +01:00
this . renderProductIconThemes ( content , manifest , layout ) ,
2019-07-16 20:30:25 +02:00
this . renderColors ( content , manifest , layout ) ,
this . renderJSONValidation ( content , manifest , layout ) ,
this . renderDebuggers ( content , manifest , layout ) ,
this . renderViewContainers ( content , manifest , layout ) ,
this . renderViews ( content , manifest , layout ) ,
2019-10-16 08:21:35 +02:00
this . renderLocalizations ( content , manifest , layout ) ,
2019-10-16 22:18:31 +02:00
this . renderCustomEditors ( content , manifest , layout ) ,
2021-11-04 03:51:59 +01:00
this . renderNotebooks ( content , manifest , layout ) ,
this . renderNotebookRenderers ( content , manifest , layout ) ,
2020-08-27 20:50:51 +02:00
this . renderAuthentication ( content , manifest , layout ) ,
2020-11-08 12:19:53 +01:00
this . renderActivationEvents ( content , manifest , layout ) ,
2019-07-16 20:30:25 +02:00
] ;
scrollableContent . scanDomNode ( ) ;
const isEmpty = ! renders . some ( x = > x ) ;
if ( isEmpty ) {
append ( content , $ ( 'p.nocontent' ) ) . textContent = localize ( 'noContributions' , "No Contributions" ) ;
2019-08-14 01:09:36 +02:00
append ( template . content , content ) ;
2019-07-16 20:30:25 +02:00
} else {
2019-08-14 01:09:36 +02:00
append ( template . content , scrollableContent . getDomNode ( ) ) ;
2019-07-16 20:30:25 +02:00
this . contentDisposables . add ( scrollableContent ) ;
2016-10-21 16:55:13 +02:00
}
2018-09-12 16:20:43 +02:00
return content ;
2017-11-14 21:28:56 +01:00
} , ( ) = > {
2020-11-11 08:17:27 +01:00
if ( token . isCancellationRequested ) {
return null ;
}
2018-09-03 16:00:15 +02:00
append ( content , $ ( 'p.nocontent' ) ) . textContent = localize ( 'noContributions' , "No Contributions" ) ;
2019-08-14 01:09:36 +02:00
append ( template . content , content ) ;
2018-09-12 16:20:43 +02:00
return content ;
2018-08-10 15:10:54 +02:00
} ) ;
2016-08-22 16:43:50 +02:00
}
2021-03-25 09:03:07 +01:00
private openExtensionDependencies ( extension : IExtension , template : IExtensionEditorTemplate , token : CancellationToken ) : Promise < IActiveElement | null > {
2020-11-11 08:17:27 +01:00
if ( token . isCancellationRequested ) {
return Promise . resolve ( null ) ;
}
2019-05-22 18:52:06 +02:00
if ( arrays . isFalsyOrEmpty ( extension . dependencies ) ) {
2019-08-14 01:09:36 +02:00
append ( template . content , $ ( 'p.nocontent' ) ) . textContent = localize ( 'noDependencies' , "No Dependencies" ) ;
return Promise . resolve ( template . content ) ;
2016-10-21 16:55:13 +02:00
}
2016-10-06 14:26:56 +02:00
2019-05-22 18:52:06 +02:00
const content = $ ( 'div' , { class : 'subcontent' } ) ;
const scrollableContent = new DomScrollableElement ( content , { } ) ;
2019-08-14 01:09:36 +02:00
append ( template . content , scrollableContent . getDomNode ( ) ) ;
2019-06-12 20:49:11 +02:00
this . contentDisposables . add ( scrollableContent ) ;
2018-07-11 20:00:56 +02:00
2019-11-18 17:37:04 +01:00
const dependenciesTree = this . instantiationService . createInstance ( ExtensionsTree ,
new ExtensionData ( extension , null , extension = > extension . dependencies || [ ] , this . extensionsWorkbenchService ) , content ,
{
listBackground : editorBackground
} ) ;
2019-05-22 18:52:06 +02:00
const layout = ( ) = > {
scrollableContent . scanDomNode ( ) ;
const scrollDimensions = scrollableContent . getScrollDimensions ( ) ;
2021-03-25 09:03:07 +01:00
dependenciesTree . layout ( scrollDimensions . height ) ;
} ;
const removeLayoutParticipant = arrays . insert ( this . layoutParticipants , { layout } ) ;
this . contentDisposables . add ( toDisposable ( removeLayoutParticipant ) ) ;
this . contentDisposables . add ( dependenciesTree ) ;
scrollableContent . scanDomNode ( ) ;
return Promise . resolve ( { focus() { dependenciesTree . domFocus ( ) ; } } ) ;
}
2021-07-19 16:08:39 +02:00
private async openExtensionPack ( extension : IExtension , template : IExtensionEditorTemplate , token : CancellationToken ) : Promise < IActiveElement | null > {
2021-03-25 09:03:07 +01:00
if ( token . isCancellationRequested ) {
return Promise . resolve ( null ) ;
}
2021-07-19 16:08:39 +02:00
const manifest = await this . loadContents ( ( ) = > this . extensionManifest ! . get ( ) , template . content ) ;
if ( token . isCancellationRequested ) {
return null ;
2021-03-25 09:03:07 +01:00
}
2021-07-19 16:08:39 +02:00
if ( ! manifest ) {
return null ;
}
return this . renderExtensionPack ( manifest , template . content , token ) ;
2018-07-11 20:00:56 +02:00
}
2021-07-15 00:01:13 +02:00
private async openRuntimeStatus ( extension : IExtension , template : IExtensionEditorTemplate , token : CancellationToken ) : Promise < IActiveElement | null > {
2021-07-14 14:18:32 +02:00
const content = $ ( 'div' , { class : 'subcontent' , tabindex : '0' } ) ;
const scrollableContent = new DomScrollableElement ( content , { } ) ;
const layout = ( ) = > scrollableContent . scanDomNode ( ) ;
const removeLayoutParticipant = arrays . insert ( this . layoutParticipants , { layout } ) ;
this . contentDisposables . add ( toDisposable ( removeLayoutParticipant ) ) ;
const updateContent = ( ) = > {
scrollableContent . scanDomNode ( ) ;
reset ( content , this . renderRuntimeStatus ( extension , layout ) ) ;
} ;
updateContent ( ) ;
this . extensionService . onDidChangeExtensionsStatus ( e = > {
if ( e . some ( extensionIdentifier = > areSameExtensions ( { id : extensionIdentifier.value } , extension . identifier ) ) ) {
updateContent ( ) ;
}
} , this , this . contentDisposables ) ;
2021-07-15 01:51:59 +02:00
this . contentDisposables . add ( scrollableContent ) ;
2021-07-14 14:18:32 +02:00
append ( template . content , scrollableContent . getDomNode ( ) ) ;
return content ;
}
private renderRuntimeStatus ( extension : IExtension , onDetailsToggle : Function ) : HTMLElement {
const extensionStatus = this . extensionsWorkbenchService . getExtensionStatus ( extension ) ;
const element = $ ( '.runtime-status' ) ;
if ( extensionStatus ? . activationTimes ) {
const activationTime = extensionStatus . activationTimes . codeLoadingTime + extensionStatus . activationTimes . activateCallTime ;
2021-07-15 20:24:44 +02:00
append ( element , $ ( 'div.activation-message' , undefined , ` ${ localize ( 'activation' , "Activation time" ) } ${ extensionStatus . activationTimes . activationReason . startup ? ` ( ${ localize ( 'startup' , "Startup" ) } ) ` : '' } : ${ activationTime } ms ` ) ) ;
2021-07-14 14:18:32 +02:00
}
else if ( extension . local && ( extension . local . manifest . main || extension . local . manifest . browser ) ) {
append ( element , $ ( 'div.activation-message' , undefined , localize ( 'not yet activated' , "Not yet activated." ) ) ) ;
}
if ( extensionStatus ? . runtimeErrors . length ) {
append ( element , $ ( 'details' , { open : true , ontoggle : onDetailsToggle } ,
$ ( 'summary' , { tabindex : '0' } , localize ( 'uncaught errors' , "Uncaught Errors ({0})" , extensionStatus . runtimeErrors . length ) ) ,
$ ( 'div' , undefined ,
. . . extensionStatus . runtimeErrors . map ( error = > $ ( 'div.message-entry' , undefined ,
$ ( ` span ${ ThemeIcon . asCSSSelector ( errorIcon ) } ` , undefined ) ,
$ ( 'span' , undefined , getErrorMessage ( error ) ) ,
) )
) ,
) ) ;
}
if ( extensionStatus ? . messages . length ) {
append ( element , $ ( 'details' , { open : true , ontoggle : onDetailsToggle } ,
$ ( 'summary' , { tabindex : '0' } , localize ( 'messages' , "Messages ({0})" , extensionStatus ? . messages . length ) ) ,
$ ( 'div' , undefined ,
. . . extensionStatus . messages . sort ( ( a , b ) = > b . type - a . type )
. map ( message = > $ ( 'div.message-entry' , undefined ,
$ ( ` span ${ ThemeIcon . asCSSSelector ( message . type === Severity . Error ? errorIcon : message.type === Severity . Warning ? warningIcon : infoIcon ) } ` , undefined ) ,
$ ( 'span' , undefined , message . message )
) )
) ,
) ) ;
}
if ( element . children . length === 0 ) {
append ( element , $ ( 'div.no-status-message' ) ) . textContent = localize ( 'noStatus' , "No status available." ) ;
}
return element ;
}
2021-07-19 16:08:39 +02:00
private async renderExtensionPack ( manifest : IExtensionManifest , parent : HTMLElement , token : CancellationToken ) : Promise < IActiveElement | null > {
2020-11-11 08:17:27 +01:00
if ( token . isCancellationRequested ) {
2021-07-19 16:08:39 +02:00
return null ;
2020-11-11 08:17:27 +01:00
}
2018-08-10 15:10:54 +02:00
const content = $ ( 'div' , { class : 'subcontent' } ) ;
2020-03-29 19:23:46 +02:00
const scrollableContent = new DomScrollableElement ( content , { useShadows : false } ) ;
append ( parent , scrollableContent . getDomNode ( ) ) ;
2018-07-11 20:00:56 +02:00
2021-03-29 16:24:46 +02:00
const extensionsGridView = this . instantiationService . createInstance ( ExtensionsGridView , content , new Delegate ( ) ) ;
2020-03-29 19:23:46 +02:00
const extensions : IExtension [ ] = await getExtensions ( manifest . extensionPack ! , this . extensionsWorkbenchService ) ;
extensionsGridView . setExtensions ( extensions ) ;
2018-08-10 15:10:54 +02:00
scrollableContent . scanDomNode ( ) ;
2020-03-29 19:23:46 +02:00
this . contentDisposables . add ( scrollableContent ) ;
this . contentDisposables . add ( extensionsGridView ) ;
this . contentDisposables . add ( toDisposable ( arrays . insert ( this . layoutParticipants , { layout : ( ) = > scrollableContent . scanDomNode ( ) } ) ) ) ;
2021-07-19 16:08:39 +02:00
return content ;
2018-07-11 20:00:56 +02:00
}
2017-02-10 10:56:58 +01:00
private renderSettings ( container : HTMLElement , manifest : IExtensionManifest , onDetailsToggle : Function ) : boolean {
2019-11-06 01:45:30 +01:00
const configuration = manifest . contributes ? . configuration ;
2019-07-09 12:28:00 +02:00
let properties : any = { } ;
2018-07-14 00:28:59 +02:00
if ( Array . isArray ( configuration ) ) {
configuration . forEach ( config = > {
properties = { . . . properties , . . . config . properties } ;
} ) ;
} else if ( configuration ) {
properties = configuration . properties ;
}
2016-08-23 17:20:35 +02:00
const contrib = properties ? Object . keys ( properties ) : [ ] ;
2016-08-23 17:08:33 +02:00
2019-07-16 20:30:25 +02:00
if ( ! contrib . length ) {
return false ;
2016-08-23 17:08:33 +02:00
}
2019-07-16 20:30:25 +02:00
const details = $ ( 'details' , { open : true , ontoggle : onDetailsToggle } ,
2019-07-29 16:45:46 +02:00
$ ( 'summary' , { tabindex : '0' } , localize ( 'settings' , "Settings ({0})" , contrib . length ) ) ,
2019-07-16 20:30:25 +02:00
$ ( 'table' , undefined ,
$ ( 'tr' , undefined ,
$ ( 'th' , undefined , localize ( 'setting name' , "Name" ) ) ,
$ ( 'th' , undefined , localize ( 'description' , "Description" ) ) ,
$ ( 'th' , undefined , localize ( 'default' , "Default" ) )
) ,
2021-10-25 10:47:43 +02:00
. . . contrib . map ( key = > {
let description : ( Node | string ) = properties [ key ] . description ;
if ( properties [ key ] . markdownDescription ) {
const { element , dispose } = renderMarkdown ( { value : properties [ key ] . markdownDescription } , { actionHandler : { callback : ( content ) = > this . openerService . open ( content ) . catch ( onUnexpectedError ) , disposables : this.contentDisposables } } ) ;
description = element ;
this . contentDisposables . add ( toDisposable ( dispose ) ) ;
}
return $ ( 'tr' , undefined ,
$ ( 'td' , undefined , $ ( 'code' , undefined , key ) ) ,
$ ( 'td' , undefined , description ) ,
$ ( 'td' , undefined , $ ( 'code' , undefined , ` ${ isUndefined ( properties [ key ] . default ) ? getDefaultValue ( properties [ key ] . type ) : properties [ key ] . default } ` ) ) ) ;
} )
2019-07-16 20:30:25 +02:00
)
) ;
append ( container , details ) ;
return true ;
2016-08-23 17:08:33 +02:00
}
2017-02-10 10:56:58 +01:00
private renderDebuggers ( container : HTMLElement , manifest : IExtensionManifest , onDetailsToggle : Function ) : boolean {
2019-11-06 01:45:30 +01:00
const contrib = manifest . contributes ? . debuggers || [ ] ;
2019-07-17 17:45:33 +02:00
if ( ! contrib . length ) {
return false ;
2016-08-23 17:08:33 +02:00
}
2019-07-17 17:45:33 +02:00
const details = $ ( 'details' , { open : true , ontoggle : onDetailsToggle } ,
2019-07-29 16:45:46 +02:00
$ ( 'summary' , { tabindex : '0' } , localize ( 'debuggers' , "Debuggers ({0})" , contrib . length ) ) ,
2019-07-17 17:45:33 +02:00
$ ( 'table' , undefined ,
$ ( 'tr' , undefined ,
$ ( 'th' , undefined , localize ( 'debugger name' , "Name" ) ) ,
$ ( 'th' , undefined , localize ( 'debugger type' , "Type" ) ) ,
) ,
. . . contrib . map ( d = > $ ( 'tr' , undefined ,
$ ( 'td' , undefined , d . label ! ) ,
$ ( 'td' , undefined , d . type ) ) )
)
) ;
append ( container , details ) ;
return true ;
2016-08-23 17:08:33 +02:00
}
2018-05-16 15:00:20 +02:00
private renderViewContainers ( container : HTMLElement , manifest : IExtensionManifest , onDetailsToggle : Function ) : boolean {
2019-11-06 01:45:30 +01:00
const contrib = manifest . contributes ? . viewsContainers || { } ;
2018-05-16 15:00:20 +02:00
2019-11-06 01:45:30 +01:00
const viewContainers = Object . keys ( contrib ) . reduce ( ( result , location ) = > {
2018-05-16 15:00:20 +02:00
let viewContainersForLocation : IViewContainer [ ] = contrib [ location ] ;
result . push ( . . . viewContainersForLocation . map ( viewContainer = > ( { . . . viewContainer , location } ) ) ) ;
return result ;
2019-02-07 19:17:27 +01:00
} , [ ] as Array < { id : string , title : string , location : string } > ) ;
2018-05-16 15:00:20 +02:00
2019-07-17 17:45:33 +02:00
if ( ! viewContainers . length ) {
return false ;
}
2019-07-16 20:30:25 +02:00
2019-07-17 17:45:33 +02:00
const details = $ ( 'details' , { open : true , ontoggle : onDetailsToggle } ,
2019-07-29 16:45:46 +02:00
$ ( 'summary' , { tabindex : '0' } , localize ( 'viewContainers' , "View Containers ({0})" , viewContainers . length ) ) ,
2019-07-17 17:45:33 +02:00
$ ( 'table' , undefined ,
$ ( 'tr' , undefined , $ ( 'th' , undefined , localize ( 'view container id' , "ID" ) ) , $ ( 'th' , undefined , localize ( 'view container title' , "Title" ) ) , $ ( 'th' , undefined , localize ( 'view container location' , "Where" ) ) ) ,
. . . viewContainers . map ( viewContainer = > $ ( 'tr' , undefined , $ ( 'td' , undefined , viewContainer . id ) , $ ( 'td' , undefined , viewContainer . title ) , $ ( 'td' , undefined , viewContainer . location ) ) )
)
) ;
2019-07-17 17:25:59 +02:00
2019-07-17 17:45:33 +02:00
append ( container , details ) ;
return true ;
2018-05-16 15:00:20 +02:00
}
2017-06-02 11:08:10 +02:00
private renderViews ( container : HTMLElement , manifest : IExtensionManifest , onDetailsToggle : Function ) : boolean {
2019-11-06 01:45:30 +01:00
const contrib = manifest . contributes ? . views || { } ;
2017-06-02 11:08:10 +02:00
2019-11-06 01:45:30 +01:00
const views = Object . keys ( contrib ) . reduce ( ( result , location ) = > {
2017-06-02 11:08:10 +02:00
let viewsForLocation : IView [ ] = contrib [ location ] ;
result . push ( . . . viewsForLocation . map ( view = > ( { . . . view , location } ) ) ) ;
return result ;
2019-02-07 19:17:27 +01:00
} , [ ] as Array < { id : string , name : string , location : string } > ) ;
2017-06-02 11:08:10 +02:00
2019-07-17 17:45:33 +02:00
if ( ! views . length ) {
return false ;
}
2019-07-16 20:30:25 +02:00
2019-07-17 17:45:33 +02:00
const details = $ ( 'details' , { open : true , ontoggle : onDetailsToggle } ,
2019-07-29 16:45:46 +02:00
$ ( 'summary' , { tabindex : '0' } , localize ( 'views' , "Views ({0})" , views . length ) ) ,
2019-07-17 17:45:33 +02:00
$ ( 'table' , undefined ,
$ ( 'tr' , undefined , $ ( 'th' , undefined , localize ( 'view id' , "ID" ) ) , $ ( 'th' , undefined , localize ( 'view name' , "Name" ) ) , $ ( 'th' , undefined , localize ( 'view location' , "Where" ) ) ) ,
. . . views . map ( view = > $ ( 'tr' , undefined , $ ( 'td' , undefined , view . id ) , $ ( 'td' , undefined , view . name ) , $ ( 'td' , undefined , view . location ) ) )
)
) ;
2019-07-16 20:30:25 +02:00
2019-07-17 17:45:33 +02:00
append ( container , details ) ;
return true ;
2017-06-02 11:08:10 +02:00
}
2018-01-23 12:33:31 +01:00
private renderLocalizations ( container : HTMLElement , manifest : IExtensionManifest , onDetailsToggle : Function ) : boolean {
2019-11-06 01:45:30 +01:00
const localizations = manifest . contributes ? . localizations || [ ] ;
2019-07-17 17:45:33 +02:00
if ( ! localizations . length ) {
return false ;
}
2018-01-23 12:33:31 +01:00
2019-07-17 17:45:33 +02:00
const details = $ ( 'details' , { open : true , ontoggle : onDetailsToggle } ,
2019-07-29 16:45:46 +02:00
$ ( 'summary' , { tabindex : '0' } , localize ( 'localizations' , "Localizations ({0})" , localizations . length ) ) ,
2019-07-17 17:45:33 +02:00
$ ( 'table' , undefined ,
2021-08-14 23:36:32 +02:00
$ ( 'tr' , undefined , $ ( 'th' , undefined , localize ( 'localizations language id' , "Language ID" ) ) , $ ( 'th' , undefined , localize ( 'localizations language name' , "Language Name" ) ) , $ ( 'th' , undefined , localize ( 'localizations localized language name' , "Language Name (Localized)" ) ) ) ,
2019-07-17 17:45:33 +02:00
. . . localizations . map ( localization = > $ ( 'tr' , undefined , $ ( 'td' , undefined , localization . languageId ) , $ ( 'td' , undefined , localization . languageName || '' ) , $ ( 'td' , undefined , localization . localizedLanguageName || '' ) ) )
)
) ;
2019-07-17 17:25:59 +02:00
2019-07-17 17:45:33 +02:00
append ( container , details ) ;
return true ;
2018-01-23 12:33:31 +01:00
}
2019-10-16 22:18:31 +02:00
private renderCustomEditors ( container : HTMLElement , manifest : IExtensionManifest , onDetailsToggle : Function ) : boolean {
2020-02-25 19:55:19 +01:00
const webviewEditors = manifest . contributes ? . customEditors || [ ] ;
2019-10-16 08:21:35 +02:00
if ( ! webviewEditors . length ) {
return false ;
}
const details = $ ( 'details' , { open : true , ontoggle : onDetailsToggle } ,
2019-10-16 22:18:31 +02:00
$ ( 'summary' , { tabindex : '0' } , localize ( 'customEditors' , "Custom Editors ({0})" , webviewEditors . length ) ) ,
2019-10-16 08:21:35 +02:00
$ ( 'table' , undefined ,
$ ( 'tr' , undefined ,
2019-10-16 22:18:31 +02:00
$ ( 'th' , undefined , localize ( 'customEditors view type' , "View Type" ) ) ,
$ ( 'th' , undefined , localize ( 'customEditors priority' , "Priority" ) ) ,
$ ( 'th' , undefined , localize ( 'customEditors filenamePattern' , "Filename Pattern" ) ) ) ,
2019-10-16 08:21:35 +02:00
. . . webviewEditors . map ( webviewEditor = >
$ ( 'tr' , undefined ,
$ ( 'td' , undefined , webviewEditor . viewType ) ,
$ ( 'td' , undefined , webviewEditor . priority ) ,
$ ( 'td' , undefined , arrays . coalesce ( webviewEditor . selector . map ( x = > x . filenamePattern ) ) . join ( ', ' ) ) ) )
)
) ;
append ( container , details ) ;
return true ;
}
2019-11-07 02:46:32 +01:00
private renderCodeActions ( container : HTMLElement , manifest : IExtensionManifest , onDetailsToggle : Function ) : boolean {
const codeActions = manifest . contributes ? . codeActions || [ ] ;
if ( ! codeActions . length ) {
return false ;
}
const flatActions = arrays . flatten (
codeActions . map ( contribution = >
contribution . actions . map ( action = > ( { . . . action , languages : contribution.languages } ) ) ) ) ;
const details = $ ( 'details' , { open : true , ontoggle : onDetailsToggle } ,
$ ( 'summary' , { tabindex : '0' } , localize ( 'codeActions' , "Code Actions ({0})" , flatActions . length ) ) ,
$ ( 'table' , undefined ,
$ ( 'tr' , undefined ,
$ ( 'th' , undefined , localize ( 'codeActions.title' , "Title" ) ) ,
$ ( 'th' , undefined , localize ( 'codeActions.kind' , "Kind" ) ) ,
$ ( 'th' , undefined , localize ( 'codeActions.description' , "Description" ) ) ,
$ ( 'th' , undefined , localize ( 'codeActions.languages' , "Languages" ) ) ) ,
. . . flatActions . map ( action = >
$ ( 'tr' , undefined ,
$ ( 'td' , undefined , action . title ) ,
$ ( 'td' , undefined , $ ( 'code' , undefined , action . kind ) ) ,
$ ( 'td' , undefined , action . description ? ? '' ) ,
$ ( 'td' , undefined , . . . action . languages . map ( language = > $ ( 'code' , undefined , language ) ) ) ) )
)
) ;
append ( container , details ) ;
return true ;
}
2020-08-27 20:50:51 +02:00
private renderAuthentication ( container : HTMLElement , manifest : IExtensionManifest , onDetailsToggle : Function ) : boolean {
const authentication = manifest . contributes ? . authentication || [ ] ;
if ( ! authentication . length ) {
return false ;
}
const details = $ ( 'details' , { open : true , ontoggle : onDetailsToggle } ,
$ ( 'summary' , { tabindex : '0' } , localize ( 'authentication' , "Authentication ({0})" , authentication . length ) ) ,
$ ( 'table' , undefined ,
$ ( 'tr' , undefined ,
$ ( 'th' , undefined , localize ( 'authentication.label' , "Label" ) ) ,
$ ( 'th' , undefined , localize ( 'authentication.id' , "Id" ) )
) ,
. . . authentication . map ( action = >
$ ( 'tr' , undefined ,
$ ( 'td' , undefined , action . label ) ,
$ ( 'td' , undefined , action . id )
)
)
)
) ;
append ( container , details ) ;
return true ;
}
2017-09-11 15:43:09 +02:00
private renderColorThemes ( container : HTMLElement , manifest : IExtensionManifest , onDetailsToggle : Function ) : boolean {
2019-11-06 01:45:30 +01:00
const contrib = manifest . contributes ? . themes || [ ] ;
2019-07-17 17:45:33 +02:00
if ( ! contrib . length ) {
return false ;
}
2016-08-23 17:13:28 +02:00
2019-07-17 17:45:25 +02:00
const details = $ ( 'details' , { open : true , ontoggle : onDetailsToggle } ,
2019-07-29 16:45:46 +02:00
$ ( 'summary' , { tabindex : '0' } , localize ( 'colorThemes' , "Color Themes ({0})" , contrib . length ) ) ,
2019-07-17 17:45:25 +02:00
$ ( 'ul' , undefined , . . . contrib . map ( theme = > $ ( 'li' , undefined , theme . label ) ) )
) ;
2017-09-11 15:43:09 +02:00
2019-07-17 17:45:25 +02:00
append ( container , details ) ;
return true ;
2017-09-11 15:43:09 +02:00
}
private renderIconThemes ( container : HTMLElement , manifest : IExtensionManifest , onDetailsToggle : Function ) : boolean {
2019-11-06 01:45:30 +01:00
const contrib = manifest . contributes ? . iconThemes || [ ] ;
2019-07-17 17:45:33 +02:00
if ( ! contrib . length ) {
return false ;
}
2017-09-11 15:43:09 +02:00
2019-07-17 17:45:25 +02:00
const details = $ ( 'details' , { open : true , ontoggle : onDetailsToggle } ,
2020-04-05 15:19:39 +02:00
$ ( 'summary' , { tabindex : '0' } , localize ( 'iconThemes' , "File Icon Themes ({0})" , contrib . length ) ) ,
2019-07-17 17:45:25 +02:00
$ ( 'ul' , undefined , . . . contrib . map ( theme = > $ ( 'li' , undefined , theme . label ) ) )
) ;
2016-08-24 16:07:48 +02:00
2019-07-17 17:45:25 +02:00
append ( container , details ) ;
return true ;
2016-08-23 17:20:35 +02:00
}
2021-03-22 17:59:35 +01:00
private renderProductIconThemes ( container : HTMLElement , manifest : IExtensionManifest , onDetailsToggle : Function ) : boolean {
const contrib = manifest . contributes ? . productIconThemes || [ ] ;
if ( ! contrib . length ) {
return false ;
}
const details = $ ( 'details' , { open : true , ontoggle : onDetailsToggle } ,
$ ( 'summary' , { tabindex : '0' } , localize ( 'productThemes' , "Product Icon Themes ({0})" , contrib . length ) ) ,
$ ( 'ul' , undefined , . . . contrib . map ( theme = > $ ( 'li' , undefined , theme . label ) ) )
) ;
append ( container , details ) ;
return true ;
}
2017-09-11 15:33:06 +02:00
private renderColors ( container : HTMLElement , manifest : IExtensionManifest , onDetailsToggle : Function ) : boolean {
2019-11-06 01:45:30 +01:00
const colors = manifest . contributes ? . colors || [ ] ;
if ( ! colors . length ) {
2019-07-16 20:30:25 +02:00
return false ;
}
2019-07-16 19:52:16 +02:00
2019-07-16 20:30:25 +02:00
function colorPreview ( colorReference : string ) : Node [ ] {
let result : Node [ ] = [ ] ;
if ( colorReference && colorReference [ 0 ] === '#' ) {
let color = Color . fromHex ( colorReference ) ;
if ( color ) {
result . push ( $ ( 'span' , { class : 'colorBox' , style : 'background-color: ' + Color . Format . CSS . format ( color ) } , '' ) ) ;
2017-09-11 15:33:06 +02:00
}
}
2019-07-16 20:30:25 +02:00
result . push ( $ ( 'code' , undefined , colorReference ) ) ;
return result ;
2019-07-16 19:52:16 +02:00
}
2019-07-16 20:30:25 +02:00
const details = $ ( 'details' , { open : true , ontoggle : onDetailsToggle } ,
2019-07-29 16:45:46 +02:00
$ ( 'summary' , { tabindex : '0' } , localize ( 'colors' , "Colors ({0})" , colors . length ) ) ,
2019-07-16 20:30:25 +02:00
$ ( 'table' , undefined ,
$ ( 'tr' , undefined ,
$ ( 'th' , undefined , localize ( 'colorId' , "Id" ) ) ,
$ ( 'th' , undefined , localize ( 'description' , "Description" ) ) ,
$ ( 'th' , undefined , localize ( 'defaultDark' , "Dark Default" ) ) ,
$ ( 'th' , undefined , localize ( 'defaultLight' , "Light Default" ) ) ,
$ ( 'th' , undefined , localize ( 'defaultHC' , "High Contrast Default" ) )
) ,
. . . colors . map ( color = > $ ( 'tr' , undefined ,
$ ( 'td' , undefined , $ ( 'code' , undefined , color . id ) ) ,
$ ( 'td' , undefined , color . description ) ,
$ ( 'td' , undefined , . . . colorPreview ( color . defaults . dark ) ) ,
$ ( 'td' , undefined , . . . colorPreview ( color . defaults . light ) ) ,
$ ( 'td' , undefined , . . . colorPreview ( color . defaults . highContrast ) )
) )
)
) ;
append ( container , details ) ;
return true ;
2017-09-11 15:33:06 +02:00
}
2017-02-10 10:56:58 +01:00
private renderJSONValidation ( container : HTMLElement , manifest : IExtensionManifest , onDetailsToggle : Function ) : boolean {
2019-11-06 01:45:30 +01:00
const contrib = manifest . contributes ? . jsonValidation || [ ] ;
2019-07-16 20:30:25 +02:00
if ( ! contrib . length ) {
return false ;
2016-08-23 17:20:35 +02:00
}
2019-07-16 20:30:25 +02:00
const details = $ ( 'details' , { open : true , ontoggle : onDetailsToggle } ,
2019-07-29 16:45:46 +02:00
$ ( 'summary' , { tabindex : '0' } , localize ( 'JSON Validation' , "JSON Validation ({0})" , contrib . length ) ) ,
2019-07-16 20:30:25 +02:00
$ ( 'table' , undefined ,
$ ( 'tr' , undefined ,
$ ( 'th' , undefined , localize ( 'fileMatch' , "File Match" ) ) ,
$ ( 'th' , undefined , localize ( 'schema' , "Schema" ) )
) ,
. . . contrib . map ( v = > $ ( 'tr' , undefined ,
2020-03-06 15:49:08 +01:00
$ ( 'td' , undefined , $ ( 'code' , undefined , Array . isArray ( v . fileMatch ) ? v . fileMatch . join ( ', ' ) : v . fileMatch ) ) ,
2019-07-16 20:30:25 +02:00
$ ( 'td' , undefined , v . url )
) ) ) ) ;
append ( container , details ) ;
return true ;
2016-08-23 17:13:28 +02:00
}
2016-10-21 16:55:13 +02:00
private renderCommands ( container : HTMLElement , manifest : IExtensionManifest , onDetailsToggle : Function ) : boolean {
2019-11-06 01:45:30 +01:00
const rawCommands = manifest . contributes ? . commands || [ ] ;
2016-08-24 10:47:52 +02:00
const commands = rawCommands . map ( c = > ( {
2016-08-23 17:39:36 +02:00
id : c.command ,
title : c.title ,
2019-02-07 19:17:27 +01:00
keybindings : [ ] as ResolvedKeybinding [ ] ,
menus : [ ] as string [ ]
2016-08-23 17:39:36 +02:00
} ) ) ;
2016-08-24 11:32:09 +02:00
const byId = arrays . index ( commands , c = > c . id ) ;
2016-08-23 17:39:36 +02:00
2019-11-06 01:45:30 +01:00
const menus = manifest . contributes ? . menus || { } ;
2016-08-24 10:47:52 +02:00
2016-08-23 17:39:36 +02:00
Object . keys ( menus ) . forEach ( context = > {
menus [ context ] . forEach ( menu = > {
2016-08-24 11:32:09 +02:00
let command = byId [ menu . command ] ;
2016-08-23 17:39:36 +02:00
2019-07-17 17:49:16 +02:00
if ( command ) {
command . menus . push ( context ) ;
} else {
2016-08-23 17:50:54 +02:00
command = { id : menu.command , title : '' , keybindings : [ ] , menus : [ context ] } ;
2016-08-24 11:32:09 +02:00
byId [ command . id ] = command ;
2016-08-23 17:39:36 +02:00
commands . push ( command ) ;
}
} ) ;
} ) ;
2019-11-06 01:45:30 +01:00
const rawKeybindings = manifest . contributes ? . keybindings ? ( Array . isArray ( manifest . contributes . keybindings ) ? manifest . contributes . keybindings : [ manifest . contributes . keybindings ] ) : [ ] ;
2016-08-24 10:47:52 +02:00
2016-08-24 11:16:41 +02:00
rawKeybindings . forEach ( rawKeybinding = > {
2017-04-05 17:47:58 +02:00
const keybinding = this . resolveKeybinding ( rawKeybinding ) ;
2016-10-26 11:14:44 +02:00
2019-07-16 20:30:25 +02:00
if ( ! keybinding ) {
return ;
}
2016-10-26 11:14:44 +02:00
2019-07-16 20:30:25 +02:00
let command = byId [ rawKeybinding . command ] ;
2016-08-23 17:50:54 +02:00
2019-07-17 17:49:16 +02:00
if ( command ) {
command . keybindings . push ( keybinding ) ;
} else {
2019-07-16 20:30:25 +02:00
command = { id : rawKeybinding.command , title : '' , keybindings : [ keybinding ] , menus : [ ] } ;
byId [ command . id ] = command ;
commands . push ( command ) ;
2016-08-23 17:50:54 +02:00
}
} ) ;
2019-07-17 17:45:33 +02:00
if ( ! commands . length ) {
return false ;
}
2019-07-16 20:30:25 +02:00
2019-07-17 17:45:25 +02:00
const renderKeybinding = ( keybinding : ResolvedKeybinding ) : HTMLElement = > {
const element = $ ( '' ) ;
2021-04-13 19:56:05 +02:00
const kbl = new KeybindingLabel ( element , OS ) ;
kbl . set ( keybinding ) ;
2021-06-06 07:20:53 +02:00
this . contentDisposables . add ( attachKeybindingLabelStyler ( kbl , this . themeService ) ) ;
2019-07-17 17:45:25 +02:00
return element ;
} ;
2019-07-16 20:30:25 +02:00
2019-07-17 17:45:25 +02:00
const details = $ ( 'details' , { open : true , ontoggle : onDetailsToggle } ,
2019-07-29 16:45:46 +02:00
$ ( 'summary' , { tabindex : '0' } , localize ( 'commands' , "Commands ({0})" , commands . length ) ) ,
2019-07-17 17:45:25 +02:00
$ ( 'table' , undefined ,
$ ( 'tr' , undefined ,
$ ( 'th' , undefined , localize ( 'command name' , "Name" ) ) ,
$ ( 'th' , undefined , localize ( 'description' , "Description" ) ) ,
$ ( 'th' , undefined , localize ( 'keyboard shortcuts' , "Keyboard Shortcuts" ) ) ,
$ ( 'th' , undefined , localize ( 'menuContexts' , "Menu Contexts" ) )
) ,
. . . commands . map ( c = > $ ( 'tr' , undefined ,
$ ( 'td' , undefined , $ ( 'code' , undefined , c . id ) ) ,
$ ( 'td' , undefined , c . title ) ,
$ ( 'td' , undefined , . . . c . keybindings . map ( keybinding = > renderKeybinding ( keybinding ) ) ) ,
$ ( 'td' , undefined , . . . c . menus . map ( context = > $ ( 'code' , undefined , context ) ) )
) )
)
) ;
2019-07-16 20:30:25 +02:00
2019-07-17 17:45:25 +02:00
append ( container , details ) ;
return true ;
2016-08-23 17:39:36 +02:00
}
2017-02-10 10:56:58 +01:00
private renderLanguages ( container : HTMLElement , manifest : IExtensionManifest , onDetailsToggle : Function ) : boolean {
2016-08-24 11:41:21 +02:00
const contributes = manifest . contributes ;
2019-11-06 01:45:30 +01:00
const rawLanguages = contributes ? . languages || [ ] ;
2016-08-24 11:25:29 +02:00
const languages = rawLanguages . map ( l = > ( {
id : l.id ,
2016-08-24 11:32:09 +02:00
name : ( l . aliases || [ ] ) [ 0 ] || l . id ,
2016-08-24 11:41:21 +02:00
extensions : l.extensions || [ ] ,
hasGrammar : false ,
hasSnippets : false
2016-08-24 11:25:29 +02:00
} ) ) ;
2016-08-24 11:32:09 +02:00
const byId = arrays . index ( languages , l = > l . id ) ;
2019-11-06 01:45:30 +01:00
const grammars = contributes ? . grammars || [ ] ;
2016-08-24 11:32:09 +02:00
grammars . forEach ( grammar = > {
let language = byId [ grammar . language ] ;
2019-07-17 17:49:16 +02:00
if ( language ) {
language . hasGrammar = true ;
} else {
2016-08-24 11:41:21 +02:00
language = { id : grammar.language , name : grammar.language , extensions : [ ] , hasGrammar : true , hasSnippets : false } ;
2016-08-24 11:32:09 +02:00
byId [ language . id ] = language ;
languages . push ( language ) ;
}
} ) ;
2019-11-06 01:45:30 +01:00
const snippets = contributes ? . snippets || [ ] ;
2016-08-24 11:41:21 +02:00
snippets . forEach ( snippet = > {
let language = byId [ snippet . language ] ;
2019-07-17 17:49:16 +02:00
if ( language ) {
language . hasSnippets = true ;
} else {
2016-08-24 11:41:21 +02:00
language = { id : snippet.language , name : snippet.language , extensions : [ ] , hasGrammar : false , hasSnippets : true } ;
byId [ language . id ] = language ;
languages . push ( language ) ;
}
} ) ;
2019-07-17 17:45:33 +02:00
if ( ! languages . length ) {
return false ;
}
2019-07-16 20:30:25 +02:00
2019-07-17 17:45:25 +02:00
const details = $ ( 'details' , { open : true , ontoggle : onDetailsToggle } ,
2019-07-29 16:45:46 +02:00
$ ( 'summary' , { tabindex : '0' } , localize ( 'languages' , "Languages ({0})" , languages . length ) ) ,
2019-07-17 17:45:25 +02:00
$ ( 'table' , undefined ,
$ ( 'tr' , undefined ,
$ ( 'th' , undefined , localize ( 'language id' , "ID" ) ) ,
$ ( 'th' , undefined , localize ( 'language name' , "Name" ) ) ,
$ ( 'th' , undefined , localize ( 'file extensions' , "File Extensions" ) ) ,
$ ( 'th' , undefined , localize ( 'grammar' , "Grammar" ) ) ,
$ ( 'th' , undefined , localize ( 'snippets' , "Snippets" ) )
) ,
. . . languages . map ( l = > $ ( 'tr' , undefined ,
$ ( 'td' , undefined , l . id ) ,
$ ( 'td' , undefined , l . name ) ,
$ ( 'td' , undefined , . . . join ( l . extensions . map ( ext = > $ ( 'code' , undefined , ext ) ) , ' ' ) ) ,
2021-11-20 21:01:29 +01:00
$ ( 'td' , undefined , document . createTextNode ( l . hasGrammar ? '✔︎' : '\u2014' ) ) ,
$ ( 'td' , undefined , document . createTextNode ( l . hasSnippets ? '✔︎' : '\u2014' ) )
2019-07-17 17:45:25 +02:00
) )
)
) ;
2019-07-16 20:30:25 +02:00
2019-07-17 17:45:25 +02:00
append ( container , details ) ;
return true ;
2016-08-24 11:25:29 +02:00
}
2020-11-08 12:19:53 +01:00
private renderActivationEvents ( container : HTMLElement , manifest : IExtensionManifest , onDetailsToggle : Function ) : boolean {
const activationEvents = manifest . activationEvents || [ ] ;
if ( ! activationEvents . length ) {
return false ;
}
const details = $ ( 'details' , { open : true , ontoggle : onDetailsToggle } ,
$ ( 'summary' , { tabindex : '0' } , localize ( 'activation events' , "Activation Events ({0})" , activationEvents . length ) ) ,
2020-11-12 09:20:05 +01:00
$ ( 'ul' , undefined , . . . activationEvents . map ( activationEvent = > $ ( 'li' , undefined , $ ( 'code' , undefined , activationEvent ) ) ) )
2020-11-08 12:19:53 +01:00
) ;
append ( container , details ) ;
return true ;
}
2021-11-04 03:51:59 +01:00
private renderNotebooks ( container : HTMLElement , manifest : IExtensionManifest , onDetailsToggle : Function ) : boolean {
const contrib = manifest . contributes ? . notebooks || [ ] ;
if ( ! contrib . length ) {
return false ;
}
const details = $ ( 'details' , { open : true , ontoggle : onDetailsToggle } ,
$ ( 'summary' , { tabindex : '0' } , localize ( 'Notebooks' , "Notebooks ({0})" , contrib . length ) ) ,
$ ( 'table' , undefined ,
$ ( 'tr' , undefined ,
$ ( 'th' , undefined , localize ( 'Notebook id' , "Id" ) ) ,
$ ( 'th' , undefined , localize ( 'Notebook name' , "Name" ) ) ,
) ,
. . . contrib . map ( d = > $ ( 'tr' , undefined ,
$ ( 'td' , undefined , d . type ) ,
$ ( 'td' , undefined , d . displayName ) ) )
)
) ;
append ( container , details ) ;
return true ;
}
private renderNotebookRenderers ( container : HTMLElement , manifest : IExtensionManifest , onDetailsToggle : Function ) : boolean {
const contrib = manifest . contributes ? . notebookRenderer || [ ] ;
if ( ! contrib . length ) {
return false ;
}
const details = $ ( 'details' , { open : true , ontoggle : onDetailsToggle } ,
$ ( 'summary' , { tabindex : '0' } , localize ( 'NotebookRenderers' , "Notebook Renderers ({0})" , contrib . length ) ) ,
$ ( 'table' , undefined ,
$ ( 'tr' , undefined ,
$ ( 'th' , undefined , localize ( 'Notebook renderer name' , "Name" ) ) ,
2021-11-04 05:19:53 +01:00
$ ( 'th' , undefined , localize ( 'Notebook mimetypes' , "Mimetypes" ) ) ,
2021-11-04 03:51:59 +01:00
) ,
. . . contrib . map ( d = > $ ( 'tr' , undefined ,
$ ( 'td' , undefined , d . displayName ) ,
$ ( 'td' , undefined , d . mimeTypes . join ( ',' ) ) ) )
)
) ;
append ( container , details ) ;
return true ;
}
2019-02-07 19:17:27 +01:00
private resolveKeybinding ( rawKeyBinding : IKeyBinding ) : ResolvedKeybinding | null {
let key : string | undefined ;
2016-08-24 11:16:41 +02:00
2019-08-15 14:37:08 +02:00
switch ( platform ) {
2016-08-24 11:16:41 +02:00
case 'win32' : key = rawKeyBinding . win ; break ;
case 'linux' : key = rawKeyBinding . linux ; break ;
case 'darwin' : key = rawKeyBinding . mac ; break ;
}
2021-10-15 21:52:46 +02:00
return this . keybindingService . resolveUserBinding ( key || rawKeyBinding . key ) [ 0 ] ;
2016-08-24 11:16:41 +02:00
}
2021-07-15 00:01:13 +02:00
private loadContents < T > ( loadingTask : ( ) = > CacheResult < T > , container : HTMLElement ) : Promise < T > {
container . classList . add ( 'loading' ) ;
2016-04-19 10:07:48 +02:00
2019-10-16 22:30:10 +02:00
const result = this . contentDisposables . add ( loadingTask ( ) ) ;
2021-07-15 00:01:13 +02:00
const onDone = ( ) = > container . classList . remove ( 'loading' ) ;
2018-08-10 15:10:54 +02:00
result . promise . then ( onDone , onDone ) ;
return result . promise ;
2016-04-19 09:37:45 +02:00
}
2021-07-26 11:23:08 +02:00
layout ( dimension : Dimension ) : void {
this . dimension = dimension ;
2016-08-24 16:07:48 +02:00
this . layoutParticipants . forEach ( p = > p . layout ( ) ) ;
2016-04-19 09:37:45 +02:00
}
2016-08-31 11:28:47 +02:00
private onError ( err : any ) : void {
if ( isPromiseCanceledError ( err ) ) {
return ;
}
2019-07-16 20:30:25 +02:00
2018-02-19 17:57:12 +01:00
this . notificationService . error ( err ) ;
2016-08-31 11:28:47 +02:00
}
2016-04-19 09:37:45 +02:00
}
2017-07-07 22:20:53 +02:00
2021-02-26 14:08:37 +01:00
const contextKeyExpr = ContextKeyExpr . and ( ContextKeyExpr . equals ( 'activeEditor' , ExtensionEditor . ID ) , EditorContextKeys . focus . toNegated ( ) ) ;
2020-02-06 10:10:21 +01:00
registerAction2 ( class ShowExtensionEditorFindAction extends Action2 {
constructor ( ) {
super ( {
id : 'editor.action.extensioneditor.showfind' ,
title : localize ( 'find' , "Find" ) ,
keybinding : {
when : contextKeyExpr ,
weight : KeybindingWeight.EditorContrib ,
2021-10-19 15:37:51 +02:00
primary : KeyMod.CtrlCmd | KeyCode . KeyF ,
2020-02-06 10:10:21 +01:00
}
} ) ;
}
run ( accessor : ServicesAccessor ) : any {
2019-08-21 20:47:34 +02:00
const extensionEditor = getExtensionEditor ( accessor ) ;
2017-08-28 15:24:32 +02:00
if ( extensionEditor ) {
extensionEditor . showFind ( ) ;
}
}
2020-02-06 10:10:21 +01:00
} ) ;
registerAction2 ( class StartExtensionEditorFindNextAction extends Action2 {
constructor ( ) {
super ( {
id : 'editor.action.extensioneditor.findNext' ,
title : localize ( 'find next' , "Find Next" ) ,
keybinding : {
when : ContextKeyExpr.and (
contextKeyExpr ,
KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED ) ,
primary : KeyCode.Enter ,
weight : KeybindingWeight.EditorContrib
}
} ) ;
2019-08-21 20:47:34 +02:00
}
2020-02-06 10:10:21 +01:00
run ( accessor : ServicesAccessor ) : any {
2019-08-21 20:47:34 +02:00
const extensionEditor = getExtensionEditor ( accessor ) ;
if ( extensionEditor ) {
extensionEditor . runFindAction ( false ) ;
2017-08-28 15:24:32 +02:00
}
}
2020-02-06 10:10:21 +01:00
} ) ;
registerAction2 ( class StartExtensionEditorFindPreviousAction extends Action2 {
constructor ( ) {
super ( {
id : 'editor.action.extensioneditor.findPrevious' ,
title : localize ( 'find previous' , "Find Previous" ) ,
keybinding : {
when : ContextKeyExpr.and (
contextKeyExpr ,
KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED ) ,
primary : KeyMod.Shift | KeyCode . Enter ,
weight : KeybindingWeight.EditorContrib
}
} ) ;
2017-08-28 15:24:32 +02:00
}
2020-02-06 10:10:21 +01:00
run ( accessor : ServicesAccessor ) : any {
2019-08-21 20:47:34 +02:00
const extensionEditor = getExtensionEditor ( accessor ) ;
if ( extensionEditor ) {
extensionEditor . runFindAction ( true ) ;
}
}
2020-02-06 10:10:21 +01:00
} ) ;
2019-08-21 20:47:34 +02:00
2021-06-11 12:15:58 +02:00
registerThemingParticipant ( ( theme : IColorTheme , collector : ICssStyleCollector ) = > {
const link = theme . getColor ( textLinkForeground ) ;
if ( link ) {
2021-07-28 14:57:50 +02:00
collector . addRule ( ` .monaco-workbench .extension-editor .content .details .additional-details-container .resources-container a { color: ${ link } ; } ` ) ;
collector . addRule ( ` .monaco-workbench .extension-editor .content .feature-contributions a { color: ${ link } ; } ` ) ;
2021-06-11 12:15:58 +02:00
}
const activeLink = theme . getColor ( textLinkActiveForeground ) ;
if ( activeLink ) {
2021-07-28 14:57:50 +02:00
collector . addRule ( ` .monaco-workbench .extension-editor .content .details .additional-details-container .resources-container a:hover,
. monaco - workbench . extension - editor . content . details . additional - details - container . resources - container a :active { color : $ { activeLink } ; } ` );
collector . addRule ( ` .monaco-workbench .extension-editor .content .feature-contributions a:hover,
. monaco - workbench . extension - editor . content . feature - contributions a :active { color : $ { activeLink } ; } ` );
2021-06-11 12:15:58 +02:00
}
2021-07-15 00:01:13 +02:00
const buttonHoverBackgroundColor = theme . getColor ( buttonHoverBackground ) ;
if ( buttonHoverBackgroundColor ) {
2021-07-15 01:51:59 +02:00
collector . addRule ( ` .monaco-workbench .extension-editor .content > .details > .additional-details-container .categories-container > .categories > .category:hover { background-color: ${ buttonHoverBackgroundColor } ; border-color: ${ buttonHoverBackgroundColor } ; } ` ) ;
collector . addRule ( ` .monaco-workbench .extension-editor .content > .details > .additional-details-container .tags-container > .tags > .tag:hover { background-color: ${ buttonHoverBackgroundColor } ; border-color: ${ buttonHoverBackgroundColor } ; } ` ) ;
2021-07-15 00:01:13 +02:00
}
const buttonForegroundColor = theme . getColor ( buttonForeground ) ;
if ( buttonForegroundColor ) {
2021-07-15 01:51:59 +02:00
collector . addRule ( ` .monaco-workbench .extension-editor .content > .details > .additional-details-container .categories-container > .categories > .category:hover { color: ${ buttonForegroundColor } ; } ` ) ;
collector . addRule ( ` .monaco-workbench .extension-editor .content > .details > .additional-details-container .tags-container > .tags > .tag:hover { color: ${ buttonForegroundColor } ; } ` ) ;
2021-07-15 00:01:13 +02:00
}
2021-06-11 12:15:58 +02:00
} ) ;
2019-08-21 20:47:34 +02:00
function getExtensionEditor ( accessor : ServicesAccessor ) : ExtensionEditor | null {
2020-03-03 10:47:50 +01:00
const activeEditorPane = accessor . get ( IEditorService ) . activeEditorPane ;
if ( activeEditorPane instanceof ExtensionEditor ) {
return activeEditorPane ;
2019-08-21 20:47:34 +02:00
}
return null ;
}