@theia/plugin-ext 1.53.0-next.55 → 1.53.0-next.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -54
- package/lib/common/plugin-api-rpc.d.ts +1 -4
- package/lib/common/plugin-api-rpc.d.ts.map +1 -1
- package/lib/common/plugin-api-rpc.js.map +1 -1
- package/lib/hosted/browser/hosted-plugin.js +9 -9
- package/lib/main/browser/editors-and-documents-main.d.ts.map +1 -1
- package/lib/main/browser/editors-and-documents-main.js +14 -2
- package/lib/main/browser/editors-and-documents-main.js.map +1 -1
- package/lib/main/browser/notebooks/notebook-documents-and-editors-main.d.ts.map +1 -1
- package/lib/main/browser/notebooks/notebook-documents-and-editors-main.js +2 -0
- package/lib/main/browser/notebooks/notebook-documents-and-editors-main.js.map +1 -1
- package/lib/main/browser/notebooks/renderers/cell-output-webview.js +86 -86
- package/lib/main/browser/plugin-icon-theme-service.js +20 -20
- package/lib/main/browser/plugin-shared-style.js +14 -14
- package/lib/main/browser/tabs/tabs-main.d.ts +1 -1
- package/lib/main/browser/tabs/tabs-main.d.ts.map +1 -1
- package/lib/main/browser/tabs/tabs-main.js +1 -1
- package/lib/main/browser/tabs/tabs-main.js.map +1 -1
- package/lib/main/browser/text-editor-main.d.ts +6 -3
- package/lib/main/browser/text-editor-main.d.ts.map +1 -1
- package/lib/main/browser/text-editor-main.js +39 -5
- package/lib/main/browser/text-editor-main.js.map +1 -1
- package/lib/main/browser/view/plugin-view-registry.js +3 -3
- package/lib/main/browser/webview/webview-frontend-security-warnings.js +2 -2
- package/lib/main/browser/webview/webview-secondary-window-support.js +10 -10
- package/lib/main/browser/workspace-main.d.ts +0 -2
- package/lib/main/browser/workspace-main.d.ts.map +1 -1
- package/lib/main/node/plugin-service.d.ts +1 -1
- package/lib/main/node/webview-backend-security-warnings.js +6 -6
- package/lib/plugin/languages.d.ts +0 -1
- package/lib/plugin/languages.d.ts.map +1 -1
- package/lib/plugin/notebook/notebook-kernels.d.ts +0 -2
- package/lib/plugin/notebook/notebook-kernels.d.ts.map +1 -1
- package/lib/plugin/notebook/notebooks.d.ts +1 -0
- package/lib/plugin/notebook/notebooks.d.ts.map +1 -1
- package/lib/plugin/notebook/notebooks.js +22 -2
- package/lib/plugin/notebook/notebooks.js.map +1 -1
- package/lib/plugin/scm.d.ts +0 -1
- package/lib/plugin/scm.d.ts.map +1 -1
- package/lib/plugin/terminal-ext.d.ts +0 -1
- package/lib/plugin/terminal-ext.d.ts.map +1 -1
- package/lib/plugin/text-editor.d.ts +1 -1
- package/lib/plugin/text-editor.d.ts.map +1 -1
- package/lib/plugin/text-editor.js.map +1 -1
- package/lib/plugin/type-converters.d.ts +0 -1
- package/lib/plugin/type-converters.d.ts.map +1 -1
- package/lib/plugin/types-impl.d.ts +2 -3
- package/lib/plugin/types-impl.d.ts.map +1 -1
- package/lib/plugin/workspace.d.ts +0 -4
- package/lib/plugin/workspace.d.ts.map +1 -1
- package/package.json +28 -28
- package/src/common/arrays.ts +70 -70
- package/src/common/assert.ts +23 -23
- package/src/common/cache.ts +51 -51
- package/src/common/character-classifier.ts +73 -73
- package/src/common/collections.ts +54 -54
- package/src/common/commands.ts +19 -19
- package/src/common/connection.ts +137 -137
- package/src/common/disposable-util.ts +39 -39
- package/src/common/editor-options.ts +74 -74
- package/src/common/env.ts +19 -19
- package/src/common/errors.ts +63 -63
- package/src/common/id-generator.ts +26 -26
- package/src/common/index.ts +24 -24
- package/src/common/language-pack-service.ts +34 -34
- package/src/common/link-computer.ts +354 -354
- package/src/common/object-identifier.ts +33 -33
- package/src/common/objects.ts +50 -50
- package/src/common/paths-util.ts +158 -158
- package/src/common/plugin-api-rpc-model.ts +907 -907
- package/src/common/plugin-api-rpc.ts +2753 -2752
- package/src/common/plugin-ext-api-contribution.ts +115 -115
- package/src/common/plugin-identifiers.ts +84 -84
- package/src/common/plugin-protocol.ts +1094 -1094
- package/src/common/reference-map.ts +38 -38
- package/src/common/rpc-protocol.ts +306 -306
- package/src/common/semantic-tokens-dto.ts +182 -182
- package/src/common/test-types.ts +154 -154
- package/src/common/types.ts +129 -129
- package/src/common/uint.ts +37 -37
- package/src/common/uri-components.ts +81 -81
- package/src/hosted/browser/hosted-plugin-watcher.ts +54 -54
- package/src/hosted/browser/hosted-plugin.ts +635 -635
- package/src/hosted/browser/plugin-worker.ts +52 -52
- package/src/hosted/browser/worker/debug-stub.ts +29 -29
- package/src/hosted/browser/worker/plugin-manifest-loader.ts +114 -114
- package/src/hosted/browser/worker/worker-env-ext.ts +40 -40
- package/src/hosted/browser/worker/worker-main.ts +212 -212
- package/src/hosted/browser/worker/worker-plugin-module.ts +80 -80
- package/src/hosted/common/hosted-plugin.ts +456 -456
- package/src/hosted/node/hosted-plugin-cli-contribution.ts +75 -75
- package/src/hosted/node/hosted-plugin-deployer-handler.ts +274 -274
- package/src/hosted/node/hosted-plugin-localization-service.ts +410 -410
- package/src/hosted/node/hosted-plugin-process.ts +248 -248
- package/src/hosted/node/hosted-plugin-protocol.ts +49 -49
- package/src/hosted/node/hosted-plugin.ts +116 -116
- package/src/hosted/node/metadata-scanner.ts +64 -64
- package/src/hosted/node/plugin-activation-events.ts +112 -112
- package/src/hosted/node/plugin-ext-hosted-backend-module.ts +94 -94
- package/src/hosted/node/plugin-host-module.ts +69 -69
- package/src/hosted/node/plugin-host-proxy.ts +82 -82
- package/src/hosted/node/plugin-host-rpc.ts +377 -377
- package/src/hosted/node/plugin-host.ts +110 -110
- package/src/hosted/node/plugin-language-pack-service.ts +43 -43
- package/src/hosted/node/plugin-manifest-loader.ts +32 -32
- package/src/hosted/node/plugin-reader.ts +136 -136
- package/src/hosted/node/plugin-service.ts +197 -197
- package/src/hosted/node/scanners/backend-init-theia.ts +71 -71
- package/src/hosted/node/scanners/file-plugin-uri-factory.ts +32 -32
- package/src/hosted/node/scanners/grammars-reader.ts +57 -57
- package/src/hosted/node/scanners/plugin-uri-factory.ts +33 -33
- package/src/hosted/node/scanners/scanner-theia.ts +963 -963
- package/src/hosted/node-electron/plugin-ext-hosted-electron-backend-module.ts +26 -26
- package/src/hosted/node-electron/scanner-theia-electron.ts +32 -32
- package/src/main/browser/authentication-main.ts +401 -401
- package/src/main/browser/clipboard-main.ts +38 -38
- package/src/main/browser/command-registry-main.ts +130 -130
- package/src/main/browser/commands.ts +104 -104
- package/src/main/browser/comments/comment-glyph-widget.ts +66 -66
- package/src/main/browser/comments/comment-thread-widget.tsx +696 -696
- package/src/main/browser/comments/comments-context-key-service.ts +68 -68
- package/src/main/browser/comments/comments-contribution.ts +268 -268
- package/src/main/browser/comments/comments-decorator.ts +110 -110
- package/src/main/browser/comments/comments-main.ts +482 -482
- package/src/main/browser/comments/comments-service.ts +205 -205
- package/src/main/browser/custom-editors/custom-editor-opener.tsx +205 -205
- package/src/main/browser/custom-editors/custom-editor-service.ts +108 -108
- package/src/main/browser/custom-editors/custom-editor-undo-redo-handler.ts +41 -41
- package/src/main/browser/custom-editors/custom-editor-widget-factory.ts +44 -44
- package/src/main/browser/custom-editors/custom-editor-widget.ts +127 -127
- package/src/main/browser/custom-editors/custom-editors-main.ts +526 -526
- package/src/main/browser/custom-editors/plugin-custom-editor-registry.ts +126 -126
- package/src/main/browser/data-transfer/data-transfer-type-converters.ts +68 -68
- package/src/main/browser/debug/debug-main.ts +397 -397
- package/src/main/browser/debug/plugin-debug-adapter-contribution.ts +48 -48
- package/src/main/browser/debug/plugin-debug-configuration-provider.ts +63 -63
- package/src/main/browser/debug/plugin-debug-service.ts +427 -427
- package/src/main/browser/debug/plugin-debug-session-contribution-registry.ts +76 -76
- package/src/main/browser/debug/plugin-debug-session-factory.ts +115 -115
- package/src/main/browser/decorations/decorations-main.ts +146 -146
- package/src/main/browser/dialogs/modal-notification.ts +112 -112
- package/src/main/browser/dialogs/style/modal-notification.css +123 -123
- package/src/main/browser/dialogs-main.ts +185 -185
- package/src/main/browser/documents-main.ts +275 -275
- package/src/main/browser/editors-and-documents-main.ts +448 -425
- package/src/main/browser/env-main.ts +60 -60
- package/src/main/browser/file-system-main-impl.ts +267 -267
- package/src/main/browser/hierarchy/hierarchy-types-converters.ts +189 -189
- package/src/main/browser/keybindings/keybindings-contribution-handler.ts +66 -66
- package/src/main/browser/label-service-main.ts +51 -51
- package/src/main/browser/languages-main.ts +1439 -1439
- package/src/main/browser/localization-main.ts +34 -34
- package/src/main/browser/main-context.ts +210 -210
- package/src/main/browser/main-file-system-event-service.ts +76 -76
- package/src/main/browser/menus/menus-contribution-handler.ts +172 -172
- package/src/main/browser/menus/plugin-menu-command-adapter.ts +358 -358
- package/src/main/browser/menus/vscode-theia-menu-mappings.ts +118 -118
- package/src/main/browser/message-registry-main.ts +43 -43
- package/src/main/browser/notebooks/notebook-documents-and-editors-main.ts +252 -249
- package/src/main/browser/notebooks/notebook-documents-main.ts +202 -202
- package/src/main/browser/notebooks/notebook-dto.ts +131 -131
- package/src/main/browser/notebooks/notebook-editors-main.ts +88 -88
- package/src/main/browser/notebooks/notebook-kernels-main.ts +339 -339
- package/src/main/browser/notebooks/notebook-renderers-main.ts +47 -47
- package/src/main/browser/notebooks/notebooks-main.ts +164 -164
- package/src/main/browser/notebooks/renderers/cell-output-webview.tsx +452 -452
- package/src/main/browser/notebooks/renderers/output-webview-internal.ts +636 -636
- package/src/main/browser/notebooks/renderers/webview-communication.ts +107 -107
- package/src/main/browser/notification-main.ts +26 -26
- package/src/main/browser/output-channel-registry-main.ts +53 -53
- package/src/main/browser/plugin-authentication-service.ts +71 -71
- package/src/main/browser/plugin-contribution-handler.ts +698 -698
- package/src/main/browser/plugin-ext-frontend-module.ts +291 -291
- package/src/main/browser/plugin-ext-widget.tsx +132 -132
- package/src/main/browser/plugin-frontend-contribution.ts +70 -70
- package/src/main/browser/plugin-frontend-view-contribution.ts +38 -38
- package/src/main/browser/plugin-icon-service.ts +92 -92
- package/src/main/browser/plugin-icon-theme-service.ts +625 -625
- package/src/main/browser/plugin-shared-style.ts +154 -154
- package/src/main/browser/plugin-storage.ts +55 -55
- package/src/main/browser/plugin-terminal-registry.ts +27 -27
- package/src/main/browser/preference-registry-main.ts +123 -123
- package/src/main/browser/quick-open-main.ts +367 -367
- package/src/main/browser/scm-main.ts +472 -472
- package/src/main/browser/secrets-main.ts +82 -82
- package/src/main/browser/selection-provider-command.ts +45 -45
- package/src/main/browser/status-bar-message-registry-main.ts +90 -90
- package/src/main/browser/style/comments.css +345 -345
- package/src/main/browser/style/index.css +84 -84
- package/src/main/browser/style/plugin-sidebar.css +73 -73
- package/src/main/browser/style/tree.css +54 -54
- package/src/main/browser/style/webview.css +55 -55
- package/src/main/browser/tabs/tabs-main.ts +388 -384
- package/src/main/browser/tasks-main.ts +268 -268
- package/src/main/browser/terminal-main.ts +370 -370
- package/src/main/browser/test-main.ts +632 -632
- package/src/main/browser/text-editor-main.ts +519 -478
- package/src/main/browser/text-editor-model-service.ts +111 -111
- package/src/main/browser/text-editors-main.ts +234 -234
- package/src/main/browser/theme-icon-override.ts +246 -246
- package/src/main/browser/theming-main.ts +42 -42
- package/src/main/browser/timeline-main.ts +80 -80
- package/src/main/browser/uri-main.ts +72 -72
- package/src/main/browser/view/dnd-file-content-store.ts +42 -42
- package/src/main/browser/view/plugin-tree-view-node-label-provider.ts +81 -81
- package/src/main/browser/view/plugin-view-registry.ts +967 -967
- package/src/main/browser/view/plugin-view-widget.ts +221 -221
- package/src/main/browser/view/tree-view-decorator-service.ts +51 -51
- package/src/main/browser/view/tree-view-widget.tsx +931 -931
- package/src/main/browser/view/tree-views-main.ts +205 -205
- package/src/main/browser/view/view-context-key-service.ts +64 -64
- package/src/main/browser/webview/pre/fake.html +14 -14
- package/src/main/browser/webview/pre/host.js +130 -130
- package/src/main/browser/webview/pre/index.html +17 -17
- package/src/main/browser/webview/pre/main.js +682 -682
- package/src/main/browser/webview/pre/service-worker.js +296 -296
- package/src/main/browser/webview/webview-context-keys.ts +62 -62
- package/src/main/browser/webview/webview-environment.ts +87 -87
- package/src/main/browser/webview/webview-frontend-security-warnings.ts +59 -59
- package/src/main/browser/webview/webview-preferences.ts +72 -72
- package/src/main/browser/webview/webview-resource-cache.ts +88 -88
- package/src/main/browser/webview/webview-secondary-window-support.ts +47 -47
- package/src/main/browser/webview/webview-theme-data-provider.ts +124 -124
- package/src/main/browser/webview/webview.ts +718 -718
- package/src/main/browser/webview-views/webview-views-main.ts +154 -154
- package/src/main/browser/webview-views/webview-views.ts +43 -43
- package/src/main/browser/webviews-main.ts +287 -287
- package/src/main/browser/window-activity-tracker.ts +96 -96
- package/src/main/browser/window-state-main.ts +85 -85
- package/src/main/browser/workspace-main.ts +424 -424
- package/src/main/common/basic-message-registry-main.ts +53 -53
- package/src/main/common/basic-notification-main.ts +86 -86
- package/src/main/common/env-main.ts +44 -44
- package/src/main/common/plugin-paths-protocol.ts +26 -26
- package/src/main/common/plugin-theia-environment.ts +36 -36
- package/src/main/common/webview-protocol.ts +28 -28
- package/src/main/electron-browser/plugin-ext-frontend-electron-module.ts +25 -25
- package/src/main/electron-browser/webview/electron-webview-widget-factory.ts +59 -59
- package/src/main/node/errors.spec.ts +37 -37
- package/src/main/node/handlers/plugin-theia-directory-handler.ts +137 -137
- package/src/main/node/handlers/plugin-theia-file-handler.ts +66 -66
- package/src/main/node/paths/const.ts +21 -21
- package/src/main/node/paths/plugin-paths-service.ts +163 -163
- package/src/main/node/plugin-cli-contribution.ts +85 -85
- package/src/main/node/plugin-deployer-contribution.ts +35 -35
- package/src/main/node/plugin-deployer-directory-handler-context-impl.ts +45 -45
- package/src/main/node/plugin-deployer-entry-impl.ts +132 -132
- package/src/main/node/plugin-deployer-file-handler-context-impl.ts +33 -33
- package/src/main/node/plugin-deployer-impl.ts +375 -375
- package/src/main/node/plugin-deployer-proxy-entry-impl.ts +96 -96
- package/src/main/node/plugin-deployer-resolver-context-impl.ts +55 -55
- package/src/main/node/plugin-ext-backend-module.ts +106 -106
- package/src/main/node/plugin-github-resolver.ts +139 -139
- package/src/main/node/plugin-http-resolver.ts +92 -92
- package/src/main/node/plugin-localization-server.ts +42 -42
- package/src/main/node/plugin-mgmt-cli-contribution.ts +64 -64
- package/src/main/node/plugin-remote-cli-contribution.ts +36 -36
- package/src/main/node/plugin-remote-copy-contribution.ts +36 -36
- package/src/main/node/plugin-server-handler.ts +69 -69
- package/src/main/node/plugin-service.ts +97 -97
- package/src/main/node/plugin-theia-deployer-participant.ts +32 -32
- package/src/main/node/plugin-uninstallation-manager.ts +74 -74
- package/src/main/node/plugins-key-value-storage.spec.ts +110 -110
- package/src/main/node/plugins-key-value-storage.ts +161 -161
- package/src/main/node/resolvers/local-directory-plugin-deployer-resolver.ts +37 -37
- package/src/main/node/resolvers/local-plugin-deployer-resolver.ts +56 -56
- package/src/main/node/temp-dir-util.ts +36 -36
- package/src/main/node/webview-backend-security-warnings.ts +45 -45
- package/src/main/style/status-bar.css +35 -35
- package/src/plugin/authentication-ext.ts +140 -140
- package/src/plugin/clipboard-ext.ts +43 -43
- package/src/plugin/command-registry.ts +219 -219
- package/src/plugin/comments.ts +549 -549
- package/src/plugin/custom-editors.ts +334 -334
- package/src/plugin/debug/debug-ext.ts +549 -549
- package/src/plugin/debug/plugin-debug-adapter-creator.ts +50 -50
- package/src/plugin/debug/plugin-debug-adapter-session.ts +106 -106
- package/src/plugin/debug/plugin-debug-adapter-tracker.ts +85 -85
- package/src/plugin/decorations.ts +140 -140
- package/src/plugin/dialogs.ts +96 -96
- package/src/plugin/document-data.ts +366 -366
- package/src/plugin/documents.ts +283 -283
- package/src/plugin/editors-and-documents.ts +176 -176
- package/src/plugin/env.ts +134 -134
- package/src/plugin/file-system-event-service-ext-impl.ts +256 -256
- package/src/plugin/file-system-ext-impl.ts +415 -415
- package/src/plugin/known-commands.spec.ts +50 -50
- package/src/plugin/known-commands.ts +429 -429
- package/src/plugin/label-service.ts +36 -36
- package/src/plugin/languages/call-hierarchy.ts +124 -124
- package/src/plugin/languages/code-action.ts +162 -162
- package/src/plugin/languages/color.ts +75 -75
- package/src/plugin/languages/completion.ts +183 -183
- package/src/plugin/languages/declaration.ts +72 -72
- package/src/plugin/languages/definition.ts +73 -73
- package/src/plugin/languages/diagnostics.ts +325 -325
- package/src/plugin/languages/document-drop-edit.ts +44 -44
- package/src/plugin/languages/document-formatting.ts +47 -47
- package/src/plugin/languages/document-highlight.ts +61 -61
- package/src/plugin/languages/evaluatable-expression.ts +47 -47
- package/src/plugin/languages/folding.ts +46 -46
- package/src/plugin/languages/hover.ts +58 -58
- package/src/plugin/languages/implementation.ts +73 -73
- package/src/plugin/languages/inlay-hints.ts +149 -149
- package/src/plugin/languages/inline-completion.ts +126 -126
- package/src/plugin/languages/inline-values.ts +50 -50
- package/src/plugin/languages/lens.ts +102 -102
- package/src/plugin/languages/link-provider.ts +81 -81
- package/src/plugin/languages/linked-editing-range.ts +48 -48
- package/src/plugin/languages/on-type-formatting.ts +50 -50
- package/src/plugin/languages/outline.ts +126 -126
- package/src/plugin/languages/range-formatting.ts +48 -48
- package/src/plugin/languages/reference.ts +58 -58
- package/src/plugin/languages/rename.ts +130 -130
- package/src/plugin/languages/selection-range.ts +80 -80
- package/src/plugin/languages/semantic-highlighting.ts +211 -211
- package/src/plugin/languages/signature.ts +82 -82
- package/src/plugin/languages/type-definition.ts +73 -73
- package/src/plugin/languages/type-hierarchy.ts +117 -117
- package/src/plugin/languages/util.ts +26 -26
- package/src/plugin/languages/workspace-symbol.ts +66 -66
- package/src/plugin/languages-utils.ts +68 -68
- package/src/plugin/languages.ts +1022 -1022
- package/src/plugin/localization-ext.ts +89 -89
- package/src/plugin/markdown-string.ts +115 -115
- package/src/plugin/message-registry.ts +70 -70
- package/src/plugin/node/debug/debug.spec.ts +98 -98
- package/src/plugin/node/debug/plugin-node-debug-adapter-creator.ts +167 -167
- package/src/plugin/node/env-node-ext.ts +64 -64
- package/src/plugin/node/plugin-container-module.ts +165 -165
- package/src/plugin/notebook/notebook-document.ts +446 -446
- package/src/plugin/notebook/notebook-documents.ts +58 -58
- package/src/plugin/notebook/notebook-editor.ts +116 -116
- package/src/plugin/notebook/notebook-editors.ts +71 -71
- package/src/plugin/notebook/notebook-kernels.ts +631 -631
- package/src/plugin/notebook/notebook-renderers.ts +71 -71
- package/src/plugin/notebook/notebooks.ts +470 -449
- package/src/plugin/notification.ts +80 -80
- package/src/plugin/output-channel/log-output-channel.ts +108 -108
- package/src/plugin/output-channel/output-channel-item.ts +73 -73
- package/src/plugin/output-channel-registry.ts +52 -52
- package/src/plugin/path.spec.ts +40 -40
- package/src/plugin/path.ts +68 -68
- package/src/plugin/plugin-context.ts +1606 -1606
- package/src/plugin/plugin-icon-path.ts +53 -53
- package/src/plugin/plugin-manager.ts +508 -508
- package/src/plugin/plugin-storage.ts +138 -138
- package/src/plugin/preference-registry.spec.ts +288 -288
- package/src/plugin/preference-registry.ts +335 -335
- package/src/plugin/prefix-sum-computer.ts +218 -218
- package/src/plugin/quick-open.ts +735 -735
- package/src/plugin/scm.ts +919 -919
- package/src/plugin/secrets-ext.ts +104 -104
- package/src/plugin/status-bar/status-bar-item.ts +193 -193
- package/src/plugin/status-bar-message-registry.ts +103 -103
- package/src/plugin/tabs.ts +431 -431
- package/src/plugin/tasks/task-provider.ts +57 -57
- package/src/plugin/tasks/tasks.ts +252 -252
- package/src/plugin/telemetry-ext.ts +298 -298
- package/src/plugin/terminal-ext.ts +569 -569
- package/src/plugin/test-item.ts +174 -174
- package/src/plugin/tests.ts +545 -545
- package/src/plugin/text-editor.ts +581 -581
- package/src/plugin/text-editors.ts +157 -157
- package/src/plugin/theming.ts +73 -73
- package/src/plugin/timeline.ts +186 -186
- package/src/plugin/tree/tree-views.ts +682 -682
- package/src/plugin/type-converters.spec.ts +476 -476
- package/src/plugin/type-converters.ts +1768 -1768
- package/src/plugin/types-impl.spec.ts +85 -85
- package/src/plugin/types-impl.ts +4011 -4011
- package/src/plugin/uri-ext.ts +60 -60
- package/src/plugin/webview-views.ts +228 -228
- package/src/plugin/webviews.ts +468 -468
- package/src/plugin/window-state.ts +75 -75
- package/src/plugin/word-helper.ts +162 -162
- package/src/plugin/workspace.ts +505 -505
- package/src/plugin-ext-backend-electron-module.ts +24 -24
- package/src/plugin-ext-backend-module.ts +24 -24
- package/src/plugin-ext-frontend-electron-module.ts +19 -19
- package/src/plugin-ext-frontend-module.ts +19 -19
|
@@ -1,696 +1,696 @@
|
|
|
1
|
-
// *****************************************************************************
|
|
2
|
-
// Copyright (C) 2020 Red Hat, Inc. and others.
|
|
3
|
-
//
|
|
4
|
-
// This program and the accompanying materials are made available under the
|
|
5
|
-
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
-
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
-
//
|
|
8
|
-
// This Source Code may also be made available under the following Secondary
|
|
9
|
-
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
-
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
-
// with the GNU Classpath Exception which is available at
|
|
12
|
-
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
-
//
|
|
14
|
-
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
-
// *****************************************************************************
|
|
16
|
-
import { MonacoEditorZoneWidget } from '@theia/monaco/lib/browser/monaco-editor-zone-widget';
|
|
17
|
-
import {
|
|
18
|
-
Comment,
|
|
19
|
-
CommentMode,
|
|
20
|
-
CommentThread,
|
|
21
|
-
CommentThreadState,
|
|
22
|
-
CommentThreadCollapsibleState
|
|
23
|
-
} from '../../../common/plugin-api-rpc-model';
|
|
24
|
-
import { CommentGlyphWidget } from './comment-glyph-widget';
|
|
25
|
-
import { BaseWidget, DISABLED_CLASS } from '@theia/core/lib/browser';
|
|
26
|
-
import * as React from '@theia/core/shared/react';
|
|
27
|
-
import { MouseTargetType } from '@theia/editor/lib/browser';
|
|
28
|
-
import { CommentsService } from './comments-service';
|
|
29
|
-
import {
|
|
30
|
-
ActionMenuNode,
|
|
31
|
-
CommandRegistry,
|
|
32
|
-
CompoundMenuNode,
|
|
33
|
-
MenuModelRegistry,
|
|
34
|
-
MenuPath
|
|
35
|
-
} from '@theia/core/lib/common';
|
|
36
|
-
import { CommentsContextKeyService } from './comments-context-key-service';
|
|
37
|
-
import { RefObject } from '@theia/core/shared/react';
|
|
38
|
-
import * as monaco from '@theia/monaco-editor-core';
|
|
39
|
-
import { createRoot, Root } from '@theia/core/shared/react-dom/client';
|
|
40
|
-
|
|
41
|
-
/*---------------------------------------------------------------------------------------------
|
|
42
|
-
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
43
|
-
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
44
|
-
*--------------------------------------------------------------------------------------------*/
|
|
45
|
-
// some code copied and modified from https://github.com/microsoft/vscode/blob/1.49.3/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts
|
|
46
|
-
|
|
47
|
-
export const COMMENT_THREAD_CONTEXT: MenuPath = ['comment_thread-context-menu'];
|
|
48
|
-
export const COMMENT_CONTEXT: MenuPath = ['comment-context-menu'];
|
|
49
|
-
export const COMMENT_TITLE: MenuPath = ['comment-title-menu'];
|
|
50
|
-
|
|
51
|
-
export class CommentThreadWidget extends BaseWidget {
|
|
52
|
-
|
|
53
|
-
protected readonly zoneWidget: MonacoEditorZoneWidget;
|
|
54
|
-
protected readonly containerNodeRoot: Root;
|
|
55
|
-
protected readonly commentGlyphWidget: CommentGlyphWidget;
|
|
56
|
-
protected readonly contextMenu: CompoundMenuNode;
|
|
57
|
-
protected readonly commentFormRef: RefObject<CommentForm> = React.createRef<CommentForm>();
|
|
58
|
-
|
|
59
|
-
protected isExpanded?: boolean;
|
|
60
|
-
|
|
61
|
-
constructor(
|
|
62
|
-
editor: monaco.editor.IStandaloneCodeEditor,
|
|
63
|
-
private _owner: string,
|
|
64
|
-
private _commentThread: CommentThread,
|
|
65
|
-
private commentService: CommentsService,
|
|
66
|
-
protected readonly menus: MenuModelRegistry,
|
|
67
|
-
protected readonly contextKeyService: CommentsContextKeyService,
|
|
68
|
-
protected readonly commands: CommandRegistry
|
|
69
|
-
) {
|
|
70
|
-
super();
|
|
71
|
-
this.toDispose.push(this.zoneWidget = new MonacoEditorZoneWidget(editor));
|
|
72
|
-
this.containerNodeRoot = createRoot(this.zoneWidget.containerNode);
|
|
73
|
-
this.toDispose.push(this.commentGlyphWidget = new CommentGlyphWidget(editor));
|
|
74
|
-
this.toDispose.push(this._commentThread.onDidChangeCollapsibleState(state => {
|
|
75
|
-
if (state === CommentThreadCollapsibleState.Expanded && !this.isExpanded) {
|
|
76
|
-
const lineNumber = this._commentThread.range.startLineNumber;
|
|
77
|
-
|
|
78
|
-
this.display({ afterLineNumber: lineNumber, afterColumn: 1, heightInLines: 2 });
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (state === CommentThreadCollapsibleState.Collapsed && this.isExpanded) {
|
|
83
|
-
this.hide();
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
}));
|
|
87
|
-
this.contextKeyService.commentIsEmpty.set(true);
|
|
88
|
-
this.toDispose.push(this.zoneWidget.editor.onMouseDown(e => this.onEditorMouseDown(e)));
|
|
89
|
-
this.toDispose.push(this.contextKeyService.onDidChange(() => {
|
|
90
|
-
const commentForm = this.commentFormRef.current;
|
|
91
|
-
if (commentForm) {
|
|
92
|
-
commentForm.update();
|
|
93
|
-
}
|
|
94
|
-
}));
|
|
95
|
-
this.toDispose.push(this._commentThread.onDidChangeCanReply(_canReply => {
|
|
96
|
-
const commentForm = this.commentFormRef.current;
|
|
97
|
-
if (commentForm) {
|
|
98
|
-
commentForm.update();
|
|
99
|
-
}
|
|
100
|
-
}));
|
|
101
|
-
this.toDispose.push(this._commentThread.onDidChangeState(_state => {
|
|
102
|
-
this.update();
|
|
103
|
-
}));
|
|
104
|
-
this.contextMenu = this.menus.getMenu(COMMENT_THREAD_CONTEXT);
|
|
105
|
-
this.contextMenu.children.map(node => node instanceof ActionMenuNode && node.when).forEach(exp => {
|
|
106
|
-
if (typeof exp === 'string') {
|
|
107
|
-
this.contextKeyService.setExpression(exp);
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
public getGlyphPosition(): number {
|
|
113
|
-
return this.commentGlyphWidget.getPosition();
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
public collapse(): void {
|
|
117
|
-
this._commentThread.collapsibleState = CommentThreadCollapsibleState.Collapsed;
|
|
118
|
-
if (this._commentThread.comments && this._commentThread.comments.length === 0) {
|
|
119
|
-
this.deleteCommentThread();
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
this.hide();
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
private deleteCommentThread(): void {
|
|
126
|
-
this.dispose();
|
|
127
|
-
this.commentService.disposeCommentThread(this.owner, this._commentThread.threadId);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
override dispose(): void {
|
|
131
|
-
super.dispose();
|
|
132
|
-
if (this.commentGlyphWidget) {
|
|
133
|
-
this.commentGlyphWidget.dispose();
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
toggleExpand(lineNumber: number): void {
|
|
138
|
-
if (this.isExpanded) {
|
|
139
|
-
this._commentThread.collapsibleState = CommentThreadCollapsibleState.Collapsed;
|
|
140
|
-
this.hide();
|
|
141
|
-
if (!this._commentThread.comments || !this._commentThread.comments.length) {
|
|
142
|
-
this.deleteCommentThread();
|
|
143
|
-
}
|
|
144
|
-
} else {
|
|
145
|
-
this._commentThread.collapsibleState = CommentThreadCollapsibleState.Expanded;
|
|
146
|
-
this.display({ afterLineNumber: lineNumber, afterColumn: 1, heightInLines: 2 });
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
override hide(): void {
|
|
151
|
-
this.zoneWidget.hide();
|
|
152
|
-
this.isExpanded = false;
|
|
153
|
-
super.hide();
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
display(options: MonacoEditorZoneWidget.Options): void {
|
|
157
|
-
this.isExpanded = true;
|
|
158
|
-
if (this._commentThread.collapsibleState && this._commentThread.collapsibleState !== CommentThreadCollapsibleState.Expanded) {
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
this.commentGlyphWidget.setLineNumber(options.afterLineNumber);
|
|
162
|
-
this._commentThread.collapsibleState = CommentThreadCollapsibleState.Expanded;
|
|
163
|
-
this.zoneWidget.show(options);
|
|
164
|
-
this.update();
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
private onEditorMouseDown(e: monaco.editor.IEditorMouseEvent): void {
|
|
168
|
-
const range = e.target.range;
|
|
169
|
-
|
|
170
|
-
if (!range) {
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (!e.event.leftButton) {
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
if (e.target.type !== MouseTargetType.GUTTER_LINE_DECORATIONS) {
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const data = e.target.detail;
|
|
183
|
-
const gutterOffsetX = data.offsetX - data.glyphMarginWidth - data.lineNumbersWidth - data.glyphMarginLeft;
|
|
184
|
-
|
|
185
|
-
// don't collide with folding and git decorations
|
|
186
|
-
if (gutterOffsetX > 14) {
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const mouseDownInfo = { lineNumber: range.startLineNumber };
|
|
191
|
-
|
|
192
|
-
const { lineNumber } = mouseDownInfo;
|
|
193
|
-
|
|
194
|
-
if (!range || range.startLineNumber !== lineNumber) {
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (e.target.type !== MouseTargetType.GUTTER_LINE_DECORATIONS) {
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (!e.target.element) {
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if (this.commentGlyphWidget && this.commentGlyphWidget.getPosition() !== lineNumber) {
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (e.target.element.className.indexOf('comment-thread') >= 0) {
|
|
211
|
-
this.toggleExpand(lineNumber);
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (this._commentThread.collapsibleState === CommentThreadCollapsibleState.Collapsed) {
|
|
216
|
-
this.display({ afterLineNumber: mouseDownInfo.lineNumber, heightInLines: 2 });
|
|
217
|
-
} else {
|
|
218
|
-
this.hide();
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
public get owner(): string {
|
|
223
|
-
return this._owner;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
public get commentThread(): CommentThread {
|
|
227
|
-
return this._commentThread;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
private getThreadLabel(): string {
|
|
231
|
-
let label: string | undefined;
|
|
232
|
-
label = this._commentThread.label;
|
|
233
|
-
|
|
234
|
-
if (label === undefined) {
|
|
235
|
-
if (this._commentThread.comments && this._commentThread.comments.length) {
|
|
236
|
-
const onlyUnique = (value: Comment, index: number, self: Comment[]) => self.indexOf(value) === index;
|
|
237
|
-
const participantsList = this._commentThread.comments.filter(onlyUnique).map(comment => `@${comment.userName}`).join(', ');
|
|
238
|
-
const resolutionState = this._commentThread.state === CommentThreadState.Resolved ? '(Resolved)' : '(Unresolved)';
|
|
239
|
-
label = `Participants: ${participantsList} ${resolutionState}`;
|
|
240
|
-
} else {
|
|
241
|
-
label = 'Start discussion';
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return label;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
override update(): void {
|
|
249
|
-
if (!this.isExpanded) {
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
this.render();
|
|
253
|
-
const headHeight = Math.ceil(this.zoneWidget.editor.getOption(monaco.editor.EditorOption.lineHeight) * 1.2);
|
|
254
|
-
const lineHeight = this.zoneWidget.editor.getOption(monaco.editor.EditorOption.lineHeight);
|
|
255
|
-
const arrowHeight = Math.round(lineHeight / 3);
|
|
256
|
-
const frameThickness = Math.round(lineHeight / 9) * 2;
|
|
257
|
-
const body = this.zoneWidget.containerNode.getElementsByClassName('body')[0];
|
|
258
|
-
|
|
259
|
-
const computedLinesNumber = Math.ceil((headHeight + body.clientHeight + arrowHeight + frameThickness + 8 /** margin bottom to avoid margin collapse */) / lineHeight);
|
|
260
|
-
this.zoneWidget.show({ afterLineNumber: this._commentThread.range.startLineNumber, heightInLines: computedLinesNumber });
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
protected render(): void {
|
|
264
|
-
const headHeight = Math.ceil(this.zoneWidget.editor.getOption(monaco.editor.EditorOption.lineHeight) * 1.2);
|
|
265
|
-
this.containerNodeRoot.render(<div className={'review-widget'}>
|
|
266
|
-
<div className={'head'} style={{ height: headHeight, lineHeight: `${headHeight}px` }}>
|
|
267
|
-
<div className={'review-title'}>
|
|
268
|
-
<span className={'filename'}>{this.getThreadLabel()}</span>
|
|
269
|
-
</div>
|
|
270
|
-
<div className={'review-actions'}>
|
|
271
|
-
<div className={'monaco-action-bar animated'}>
|
|
272
|
-
<ul className={'actions-container'} role={'toolbar'}>
|
|
273
|
-
<li className={'action-item'} role={'presentation'}>
|
|
274
|
-
<a className={'action-label codicon expand-review-action codicon-chevron-up'}
|
|
275
|
-
role={'button'}
|
|
276
|
-
tabIndex={0}
|
|
277
|
-
title={'Collapse'}
|
|
278
|
-
onClick={() => this.collapse()}
|
|
279
|
-
/>
|
|
280
|
-
</li>
|
|
281
|
-
</ul>
|
|
282
|
-
</div>
|
|
283
|
-
</div>
|
|
284
|
-
</div>
|
|
285
|
-
<div className={'body'}>
|
|
286
|
-
<div className={'comments-container'} role={'presentation'} tabIndex={0}>
|
|
287
|
-
{this._commentThread.comments?.map((comment, index) => <ReviewComment
|
|
288
|
-
key={index}
|
|
289
|
-
contextKeyService={this.contextKeyService}
|
|
290
|
-
menus={this.menus}
|
|
291
|
-
comment={comment}
|
|
292
|
-
commentForm={this.commentFormRef}
|
|
293
|
-
commands={this.commands}
|
|
294
|
-
commentThread={this._commentThread}
|
|
295
|
-
/>)}
|
|
296
|
-
</div>
|
|
297
|
-
<CommentForm contextKeyService={this.contextKeyService}
|
|
298
|
-
commands={this.commands}
|
|
299
|
-
commentThread={this._commentThread}
|
|
300
|
-
menus={this.menus}
|
|
301
|
-
widget={this}
|
|
302
|
-
ref={this.commentFormRef}
|
|
303
|
-
/>
|
|
304
|
-
</div>
|
|
305
|
-
</div>);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
namespace CommentForm {
|
|
310
|
-
export interface Props {
|
|
311
|
-
menus: MenuModelRegistry,
|
|
312
|
-
commentThread: CommentThread;
|
|
313
|
-
commands: CommandRegistry;
|
|
314
|
-
contextKeyService: CommentsContextKeyService;
|
|
315
|
-
widget: CommentThreadWidget;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
export interface State {
|
|
319
|
-
expanded: boolean
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
export class CommentForm<P extends CommentForm.Props = CommentForm.Props> extends React.Component<P, CommentForm.State> {
|
|
324
|
-
private readonly menu: CompoundMenuNode;
|
|
325
|
-
private inputRef: RefObject<HTMLTextAreaElement> = React.createRef<HTMLTextAreaElement>();
|
|
326
|
-
private inputValue: string = '';
|
|
327
|
-
private readonly getInput = () => this.inputValue;
|
|
328
|
-
private readonly clearInput: () => void = () => {
|
|
329
|
-
const input = this.inputRef.current;
|
|
330
|
-
if (input) {
|
|
331
|
-
this.inputValue = '';
|
|
332
|
-
input.value = this.inputValue;
|
|
333
|
-
this.props.contextKeyService.commentIsEmpty.set(true);
|
|
334
|
-
}
|
|
335
|
-
};
|
|
336
|
-
|
|
337
|
-
update(): void {
|
|
338
|
-
this.setState(this.state);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
protected expand = () => {
|
|
342
|
-
this.setState({ expanded: true });
|
|
343
|
-
// Wait for the widget to be rendered.
|
|
344
|
-
setTimeout(() => {
|
|
345
|
-
// Update the widget's height.
|
|
346
|
-
this.props.widget.update();
|
|
347
|
-
this.inputRef.current?.focus();
|
|
348
|
-
}, 100);
|
|
349
|
-
};
|
|
350
|
-
protected collapse = () => {
|
|
351
|
-
this.setState({ expanded: false });
|
|
352
|
-
// Wait for the widget to be rendered.
|
|
353
|
-
setTimeout(() => {
|
|
354
|
-
// Update the widget's height.
|
|
355
|
-
this.props.widget.update();
|
|
356
|
-
}, 100);
|
|
357
|
-
};
|
|
358
|
-
|
|
359
|
-
override componentDidMount(): void {
|
|
360
|
-
// Wait for the widget to be rendered.
|
|
361
|
-
setTimeout(() => {
|
|
362
|
-
this.inputRef.current?.focus();
|
|
363
|
-
}, 100);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
private readonly onInput: (event: React.FormEvent) => void = (event: React.FormEvent) => {
|
|
367
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
368
|
-
const value = (event.target as any).value;
|
|
369
|
-
if (this.inputValue.length === 0 || value.length === 0) {
|
|
370
|
-
this.props.contextKeyService.commentIsEmpty.set(value.length === 0);
|
|
371
|
-
}
|
|
372
|
-
this.inputValue = value;
|
|
373
|
-
};
|
|
374
|
-
|
|
375
|
-
constructor(props: P) {
|
|
376
|
-
super(props);
|
|
377
|
-
this.state = {
|
|
378
|
-
expanded: false
|
|
379
|
-
};
|
|
380
|
-
|
|
381
|
-
const setState = this.setState.bind(this);
|
|
382
|
-
this.setState = newState => {
|
|
383
|
-
setState(newState);
|
|
384
|
-
};
|
|
385
|
-
|
|
386
|
-
this.menu = this.props.menus.getMenu(COMMENT_THREAD_CONTEXT);
|
|
387
|
-
this.menu.children.map(node => node instanceof ActionMenuNode && node.when).forEach(exp => {
|
|
388
|
-
if (typeof exp === 'string') {
|
|
389
|
-
this.props.contextKeyService.setExpression(exp);
|
|
390
|
-
}
|
|
391
|
-
});
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
override render(): React.ReactNode {
|
|
395
|
-
const { commands, commentThread, contextKeyService } = this.props;
|
|
396
|
-
const hasExistingComments = commentThread.comments && commentThread.comments.length > 0;
|
|
397
|
-
return commentThread.canReply ? <div className={'comment-form' + (this.state.expanded || commentThread.comments && commentThread.comments.length === 0 ? ' expand' : '')}>
|
|
398
|
-
<div className={'theia-comments-input-message-container'}>
|
|
399
|
-
<textarea className={'theia-comments-input-message theia-input'}
|
|
400
|
-
spellCheck={false}
|
|
401
|
-
placeholder={hasExistingComments ? 'Reply...' : 'Type a new comment'}
|
|
402
|
-
onInput={this.onInput}
|
|
403
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
404
|
-
onBlur={(event: any) => {
|
|
405
|
-
if (event.target.value.length > 0) {
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
if (event.relatedTarget && event.relatedTarget.className === 'comments-button comments-text-button theia-button') {
|
|
409
|
-
this.state = { expanded: false };
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
this.collapse();
|
|
413
|
-
}}
|
|
414
|
-
ref={this.inputRef}>
|
|
415
|
-
</textarea>
|
|
416
|
-
</div>
|
|
417
|
-
<CommentActions menu={this.menu}
|
|
418
|
-
contextKeyService={contextKeyService}
|
|
419
|
-
commands={commands}
|
|
420
|
-
commentThread={commentThread}
|
|
421
|
-
getInput={this.getInput}
|
|
422
|
-
clearInput={this.clearInput}
|
|
423
|
-
/>
|
|
424
|
-
<button className={'review-thread-reply-button'} title={'Reply...'} onClick={this.expand}>Reply...</button>
|
|
425
|
-
</div> : null;
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
namespace ReviewComment {
|
|
430
|
-
export interface Props {
|
|
431
|
-
menus: MenuModelRegistry,
|
|
432
|
-
comment: Comment;
|
|
433
|
-
commentThread: CommentThread;
|
|
434
|
-
contextKeyService: CommentsContextKeyService;
|
|
435
|
-
commands: CommandRegistry;
|
|
436
|
-
commentForm: RefObject<CommentForm>;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
export interface State {
|
|
440
|
-
hover: boolean
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
export class ReviewComment<P extends ReviewComment.Props = ReviewComment.Props> extends React.Component<P, ReviewComment.State> {
|
|
445
|
-
|
|
446
|
-
constructor(props: P) {
|
|
447
|
-
super(props);
|
|
448
|
-
this.state = {
|
|
449
|
-
hover: false
|
|
450
|
-
};
|
|
451
|
-
|
|
452
|
-
const setState = this.setState.bind(this);
|
|
453
|
-
this.setState = newState => {
|
|
454
|
-
setState(newState);
|
|
455
|
-
};
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
protected detectHover = (element: HTMLElement | null) => {
|
|
459
|
-
if (element) {
|
|
460
|
-
window.requestAnimationFrame(() => {
|
|
461
|
-
const hover = element.matches(':hover');
|
|
462
|
-
this.setState({ hover });
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
|
-
};
|
|
466
|
-
|
|
467
|
-
protected showHover = () => this.setState({ hover: true });
|
|
468
|
-
protected hideHover = () => this.setState({ hover: false });
|
|
469
|
-
|
|
470
|
-
override render(): React.ReactNode {
|
|
471
|
-
const { comment, commentForm, contextKeyService, menus, commands, commentThread } = this.props;
|
|
472
|
-
const commentUniqueId = comment.uniqueIdInThread;
|
|
473
|
-
const { hover } = this.state;
|
|
474
|
-
contextKeyService.comment.set(comment.contextValue);
|
|
475
|
-
return <div className={'review-comment'}
|
|
476
|
-
tabIndex={-1}
|
|
477
|
-
aria-label={`${comment.userName}, ${comment.body.value}`}
|
|
478
|
-
ref={this.detectHover}
|
|
479
|
-
onMouseEnter={this.showHover}
|
|
480
|
-
onMouseLeave={this.hideHover}>
|
|
481
|
-
<div className={'avatar-container'}>
|
|
482
|
-
<img className={'avatar'} src={comment.userIconPath} />
|
|
483
|
-
</div>
|
|
484
|
-
<div className={'review-comment-contents'}>
|
|
485
|
-
<div className={'comment-title monaco-mouse-cursor-text'}>
|
|
486
|
-
<strong className={'author'}>{comment.userName}</strong>
|
|
487
|
-
<small className={'timestamp'}>{this.localeDate(comment.timestamp)}</small>
|
|
488
|
-
<span className={'isPending'}>{comment.label}</span>
|
|
489
|
-
<div className={'theia-comments-inline-actions-container'}>
|
|
490
|
-
<div className={'theia-comments-inline-actions'} role={'toolbar'}>
|
|
491
|
-
{hover && menus.getMenu(COMMENT_TITLE).children.map((node, index) => node instanceof ActionMenuNode &&
|
|
492
|
-
<CommentsInlineAction key={index} {...{ node, commands, commentThread, commentUniqueId, contextKeyService }} />)}
|
|
493
|
-
</div>
|
|
494
|
-
</div>
|
|
495
|
-
</div>
|
|
496
|
-
<CommentBody value={comment.body.value}
|
|
497
|
-
isVisible={comment.mode === undefined || comment.mode === CommentMode.Preview} />
|
|
498
|
-
<CommentEditContainer contextKeyService={contextKeyService}
|
|
499
|
-
menus={menus}
|
|
500
|
-
comment={comment}
|
|
501
|
-
commentThread={commentThread}
|
|
502
|
-
commentForm={commentForm}
|
|
503
|
-
commands={commands} />
|
|
504
|
-
</div>
|
|
505
|
-
</div>;
|
|
506
|
-
}
|
|
507
|
-
protected localeDate(timestamp: string | undefined): string {
|
|
508
|
-
if (timestamp === undefined) {
|
|
509
|
-
return '';
|
|
510
|
-
}
|
|
511
|
-
const date = new Date(timestamp);
|
|
512
|
-
if (!isNaN(date.getTime())) {
|
|
513
|
-
return date.toLocaleString();
|
|
514
|
-
}
|
|
515
|
-
return '';
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
namespace CommentBody {
|
|
520
|
-
export interface Props {
|
|
521
|
-
value: string
|
|
522
|
-
isVisible: boolean
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
export class CommentBody extends React.Component<CommentBody.Props> {
|
|
527
|
-
override render(): React.ReactNode {
|
|
528
|
-
const { value, isVisible } = this.props;
|
|
529
|
-
if (!isVisible) {
|
|
530
|
-
return false;
|
|
531
|
-
}
|
|
532
|
-
return <div className={'comment-body monaco-mouse-cursor-text'}>
|
|
533
|
-
<div>
|
|
534
|
-
<p>{value}</p>
|
|
535
|
-
</div>
|
|
536
|
-
</div>;
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
namespace CommentEditContainer {
|
|
541
|
-
export interface Props {
|
|
542
|
-
contextKeyService: CommentsContextKeyService
|
|
543
|
-
menus: MenuModelRegistry,
|
|
544
|
-
comment: Comment;
|
|
545
|
-
commentThread: CommentThread;
|
|
546
|
-
commentForm: RefObject<CommentForm>;
|
|
547
|
-
commands: CommandRegistry;
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
export class CommentEditContainer extends React.Component<CommentEditContainer.Props> {
|
|
552
|
-
private readonly inputRef: RefObject<HTMLTextAreaElement> = React.createRef<HTMLTextAreaElement>();
|
|
553
|
-
private dirtyCommentMode: CommentMode | undefined;
|
|
554
|
-
private dirtyCommentFormState: boolean | undefined;
|
|
555
|
-
|
|
556
|
-
override componentDidUpdate(prevProps: Readonly<CommentEditContainer.Props>, prevState: Readonly<{}>): void {
|
|
557
|
-
const commentFormState = this.props.commentForm.current?.state;
|
|
558
|
-
const mode = this.props.comment.mode;
|
|
559
|
-
if (this.dirtyCommentMode !== mode || (this.dirtyCommentFormState !== commentFormState?.expanded && !commentFormState?.expanded)) {
|
|
560
|
-
const currentInput = this.inputRef.current;
|
|
561
|
-
if (currentInput) {
|
|
562
|
-
// Wait for the widget to be rendered.
|
|
563
|
-
setTimeout(() => {
|
|
564
|
-
currentInput.focus();
|
|
565
|
-
currentInput.setSelectionRange(currentInput.value.length, currentInput.value.length);
|
|
566
|
-
}, 50);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
this.dirtyCommentMode = mode;
|
|
570
|
-
this.dirtyCommentFormState = commentFormState?.expanded;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
override render(): React.ReactNode {
|
|
574
|
-
const { menus, comment, commands, commentThread, contextKeyService } = this.props;
|
|
575
|
-
if (!(comment.mode === CommentMode.Editing)) {
|
|
576
|
-
return false;
|
|
577
|
-
}
|
|
578
|
-
return <div className={'edit-container'}>
|
|
579
|
-
<div className={'edit-textarea'}>
|
|
580
|
-
<div className={'theia-comments-input-message-container'}>
|
|
581
|
-
<textarea className={'theia-comments-input-message theia-input'}
|
|
582
|
-
spellCheck={false}
|
|
583
|
-
defaultValue={comment.body.value}
|
|
584
|
-
ref={this.inputRef} />
|
|
585
|
-
</div>
|
|
586
|
-
</div>
|
|
587
|
-
<div className={'form-actions'}>
|
|
588
|
-
{menus.getMenu(COMMENT_CONTEXT).children.map((node, index) => {
|
|
589
|
-
const onClick = () => {
|
|
590
|
-
commands.executeCommand(node.id, {
|
|
591
|
-
thread: commentThread,
|
|
592
|
-
commentUniqueId: comment.uniqueIdInThread,
|
|
593
|
-
text: this.inputRef.current ? this.inputRef.current.value : ''
|
|
594
|
-
});
|
|
595
|
-
};
|
|
596
|
-
return node instanceof ActionMenuNode &&
|
|
597
|
-
<CommentAction key={index} {...{ node, commands, onClick, contextKeyService }} />;
|
|
598
|
-
}
|
|
599
|
-
)}
|
|
600
|
-
</div>
|
|
601
|
-
</div>;
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
namespace CommentsInlineAction {
|
|
606
|
-
export interface Props {
|
|
607
|
-
node: ActionMenuNode;
|
|
608
|
-
commentThread: CommentThread;
|
|
609
|
-
commentUniqueId: number;
|
|
610
|
-
commands: CommandRegistry;
|
|
611
|
-
contextKeyService: CommentsContextKeyService;
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
export class CommentsInlineAction extends React.Component<CommentsInlineAction.Props> {
|
|
616
|
-
override render(): React.ReactNode {
|
|
617
|
-
const { node, commands, contextKeyService, commentThread, commentUniqueId } = this.props;
|
|
618
|
-
if (node.when && !contextKeyService.match(node.when)) {
|
|
619
|
-
return false;
|
|
620
|
-
}
|
|
621
|
-
return <div className='theia-comments-inline-action'>
|
|
622
|
-
<a className={node.icon}
|
|
623
|
-
title={node.label}
|
|
624
|
-
onClick={() => {
|
|
625
|
-
commands.executeCommand(node.id, {
|
|
626
|
-
thread: commentThread,
|
|
627
|
-
commentUniqueId
|
|
628
|
-
});
|
|
629
|
-
}} />
|
|
630
|
-
</div>;
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
namespace CommentActions {
|
|
635
|
-
export interface Props {
|
|
636
|
-
contextKeyService: CommentsContextKeyService;
|
|
637
|
-
commands: CommandRegistry;
|
|
638
|
-
menu: CompoundMenuNode;
|
|
639
|
-
commentThread: CommentThread;
|
|
640
|
-
getInput: () => string;
|
|
641
|
-
clearInput: () => void;
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
export class CommentActions extends React.Component<CommentActions.Props> {
|
|
646
|
-
override render(): React.ReactNode {
|
|
647
|
-
const { contextKeyService, commands, menu, commentThread, getInput, clearInput } = this.props;
|
|
648
|
-
return <div className={'form-actions'}>
|
|
649
|
-
{menu.children.map((node, index) => node instanceof ActionMenuNode &&
|
|
650
|
-
<CommentAction key={index}
|
|
651
|
-
commands={commands}
|
|
652
|
-
node={node}
|
|
653
|
-
onClick={() => {
|
|
654
|
-
commands.executeCommand(node.id, {
|
|
655
|
-
thread: commentThread,
|
|
656
|
-
text: getInput()
|
|
657
|
-
});
|
|
658
|
-
clearInput();
|
|
659
|
-
}}
|
|
660
|
-
contextKeyService={contextKeyService}
|
|
661
|
-
/>)}
|
|
662
|
-
</div>;
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
namespace CommentAction {
|
|
666
|
-
export interface Props {
|
|
667
|
-
contextKeyService: CommentsContextKeyService;
|
|
668
|
-
commands: CommandRegistry;
|
|
669
|
-
node: ActionMenuNode;
|
|
670
|
-
onClick: () => void;
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
export class CommentAction extends React.Component<CommentAction.Props> {
|
|
675
|
-
override render(): React.ReactNode {
|
|
676
|
-
const classNames = ['comments-button', 'comments-text-button', 'theia-button'];
|
|
677
|
-
const { node, commands, contextKeyService, onClick } = this.props;
|
|
678
|
-
if (node.when && !contextKeyService.match(node.when)) {
|
|
679
|
-
return false;
|
|
680
|
-
}
|
|
681
|
-
const isEnabled = commands.isEnabled(node.command);
|
|
682
|
-
if (!isEnabled) {
|
|
683
|
-
classNames.push(DISABLED_CLASS);
|
|
684
|
-
}
|
|
685
|
-
return <button
|
|
686
|
-
className={classNames.join(' ')}
|
|
687
|
-
tabIndex={0}
|
|
688
|
-
role={'button'}
|
|
689
|
-
onClick={() => {
|
|
690
|
-
if (isEnabled) {
|
|
691
|
-
onClick();
|
|
692
|
-
}
|
|
693
|
-
}}>{node.label}
|
|
694
|
-
</button>;
|
|
695
|
-
}
|
|
696
|
-
}
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2020 Red Hat, Inc. and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
import { MonacoEditorZoneWidget } from '@theia/monaco/lib/browser/monaco-editor-zone-widget';
|
|
17
|
+
import {
|
|
18
|
+
Comment,
|
|
19
|
+
CommentMode,
|
|
20
|
+
CommentThread,
|
|
21
|
+
CommentThreadState,
|
|
22
|
+
CommentThreadCollapsibleState
|
|
23
|
+
} from '../../../common/plugin-api-rpc-model';
|
|
24
|
+
import { CommentGlyphWidget } from './comment-glyph-widget';
|
|
25
|
+
import { BaseWidget, DISABLED_CLASS } from '@theia/core/lib/browser';
|
|
26
|
+
import * as React from '@theia/core/shared/react';
|
|
27
|
+
import { MouseTargetType } from '@theia/editor/lib/browser';
|
|
28
|
+
import { CommentsService } from './comments-service';
|
|
29
|
+
import {
|
|
30
|
+
ActionMenuNode,
|
|
31
|
+
CommandRegistry,
|
|
32
|
+
CompoundMenuNode,
|
|
33
|
+
MenuModelRegistry,
|
|
34
|
+
MenuPath
|
|
35
|
+
} from '@theia/core/lib/common';
|
|
36
|
+
import { CommentsContextKeyService } from './comments-context-key-service';
|
|
37
|
+
import { RefObject } from '@theia/core/shared/react';
|
|
38
|
+
import * as monaco from '@theia/monaco-editor-core';
|
|
39
|
+
import { createRoot, Root } from '@theia/core/shared/react-dom/client';
|
|
40
|
+
|
|
41
|
+
/*---------------------------------------------------------------------------------------------
|
|
42
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
43
|
+
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
44
|
+
*--------------------------------------------------------------------------------------------*/
|
|
45
|
+
// some code copied and modified from https://github.com/microsoft/vscode/blob/1.49.3/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts
|
|
46
|
+
|
|
47
|
+
export const COMMENT_THREAD_CONTEXT: MenuPath = ['comment_thread-context-menu'];
|
|
48
|
+
export const COMMENT_CONTEXT: MenuPath = ['comment-context-menu'];
|
|
49
|
+
export const COMMENT_TITLE: MenuPath = ['comment-title-menu'];
|
|
50
|
+
|
|
51
|
+
export class CommentThreadWidget extends BaseWidget {
|
|
52
|
+
|
|
53
|
+
protected readonly zoneWidget: MonacoEditorZoneWidget;
|
|
54
|
+
protected readonly containerNodeRoot: Root;
|
|
55
|
+
protected readonly commentGlyphWidget: CommentGlyphWidget;
|
|
56
|
+
protected readonly contextMenu: CompoundMenuNode;
|
|
57
|
+
protected readonly commentFormRef: RefObject<CommentForm> = React.createRef<CommentForm>();
|
|
58
|
+
|
|
59
|
+
protected isExpanded?: boolean;
|
|
60
|
+
|
|
61
|
+
constructor(
|
|
62
|
+
editor: monaco.editor.IStandaloneCodeEditor,
|
|
63
|
+
private _owner: string,
|
|
64
|
+
private _commentThread: CommentThread,
|
|
65
|
+
private commentService: CommentsService,
|
|
66
|
+
protected readonly menus: MenuModelRegistry,
|
|
67
|
+
protected readonly contextKeyService: CommentsContextKeyService,
|
|
68
|
+
protected readonly commands: CommandRegistry
|
|
69
|
+
) {
|
|
70
|
+
super();
|
|
71
|
+
this.toDispose.push(this.zoneWidget = new MonacoEditorZoneWidget(editor));
|
|
72
|
+
this.containerNodeRoot = createRoot(this.zoneWidget.containerNode);
|
|
73
|
+
this.toDispose.push(this.commentGlyphWidget = new CommentGlyphWidget(editor));
|
|
74
|
+
this.toDispose.push(this._commentThread.onDidChangeCollapsibleState(state => {
|
|
75
|
+
if (state === CommentThreadCollapsibleState.Expanded && !this.isExpanded) {
|
|
76
|
+
const lineNumber = this._commentThread.range.startLineNumber;
|
|
77
|
+
|
|
78
|
+
this.display({ afterLineNumber: lineNumber, afterColumn: 1, heightInLines: 2 });
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (state === CommentThreadCollapsibleState.Collapsed && this.isExpanded) {
|
|
83
|
+
this.hide();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
}));
|
|
87
|
+
this.contextKeyService.commentIsEmpty.set(true);
|
|
88
|
+
this.toDispose.push(this.zoneWidget.editor.onMouseDown(e => this.onEditorMouseDown(e)));
|
|
89
|
+
this.toDispose.push(this.contextKeyService.onDidChange(() => {
|
|
90
|
+
const commentForm = this.commentFormRef.current;
|
|
91
|
+
if (commentForm) {
|
|
92
|
+
commentForm.update();
|
|
93
|
+
}
|
|
94
|
+
}));
|
|
95
|
+
this.toDispose.push(this._commentThread.onDidChangeCanReply(_canReply => {
|
|
96
|
+
const commentForm = this.commentFormRef.current;
|
|
97
|
+
if (commentForm) {
|
|
98
|
+
commentForm.update();
|
|
99
|
+
}
|
|
100
|
+
}));
|
|
101
|
+
this.toDispose.push(this._commentThread.onDidChangeState(_state => {
|
|
102
|
+
this.update();
|
|
103
|
+
}));
|
|
104
|
+
this.contextMenu = this.menus.getMenu(COMMENT_THREAD_CONTEXT);
|
|
105
|
+
this.contextMenu.children.map(node => node instanceof ActionMenuNode && node.when).forEach(exp => {
|
|
106
|
+
if (typeof exp === 'string') {
|
|
107
|
+
this.contextKeyService.setExpression(exp);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
public getGlyphPosition(): number {
|
|
113
|
+
return this.commentGlyphWidget.getPosition();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
public collapse(): void {
|
|
117
|
+
this._commentThread.collapsibleState = CommentThreadCollapsibleState.Collapsed;
|
|
118
|
+
if (this._commentThread.comments && this._commentThread.comments.length === 0) {
|
|
119
|
+
this.deleteCommentThread();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
this.hide();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private deleteCommentThread(): void {
|
|
126
|
+
this.dispose();
|
|
127
|
+
this.commentService.disposeCommentThread(this.owner, this._commentThread.threadId);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
override dispose(): void {
|
|
131
|
+
super.dispose();
|
|
132
|
+
if (this.commentGlyphWidget) {
|
|
133
|
+
this.commentGlyphWidget.dispose();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
toggleExpand(lineNumber: number): void {
|
|
138
|
+
if (this.isExpanded) {
|
|
139
|
+
this._commentThread.collapsibleState = CommentThreadCollapsibleState.Collapsed;
|
|
140
|
+
this.hide();
|
|
141
|
+
if (!this._commentThread.comments || !this._commentThread.comments.length) {
|
|
142
|
+
this.deleteCommentThread();
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
this._commentThread.collapsibleState = CommentThreadCollapsibleState.Expanded;
|
|
146
|
+
this.display({ afterLineNumber: lineNumber, afterColumn: 1, heightInLines: 2 });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
override hide(): void {
|
|
151
|
+
this.zoneWidget.hide();
|
|
152
|
+
this.isExpanded = false;
|
|
153
|
+
super.hide();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
display(options: MonacoEditorZoneWidget.Options): void {
|
|
157
|
+
this.isExpanded = true;
|
|
158
|
+
if (this._commentThread.collapsibleState && this._commentThread.collapsibleState !== CommentThreadCollapsibleState.Expanded) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
this.commentGlyphWidget.setLineNumber(options.afterLineNumber);
|
|
162
|
+
this._commentThread.collapsibleState = CommentThreadCollapsibleState.Expanded;
|
|
163
|
+
this.zoneWidget.show(options);
|
|
164
|
+
this.update();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private onEditorMouseDown(e: monaco.editor.IEditorMouseEvent): void {
|
|
168
|
+
const range = e.target.range;
|
|
169
|
+
|
|
170
|
+
if (!range) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (!e.event.leftButton) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (e.target.type !== MouseTargetType.GUTTER_LINE_DECORATIONS) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const data = e.target.detail;
|
|
183
|
+
const gutterOffsetX = data.offsetX - data.glyphMarginWidth - data.lineNumbersWidth - data.glyphMarginLeft;
|
|
184
|
+
|
|
185
|
+
// don't collide with folding and git decorations
|
|
186
|
+
if (gutterOffsetX > 14) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const mouseDownInfo = { lineNumber: range.startLineNumber };
|
|
191
|
+
|
|
192
|
+
const { lineNumber } = mouseDownInfo;
|
|
193
|
+
|
|
194
|
+
if (!range || range.startLineNumber !== lineNumber) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (e.target.type !== MouseTargetType.GUTTER_LINE_DECORATIONS) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (!e.target.element) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (this.commentGlyphWidget && this.commentGlyphWidget.getPosition() !== lineNumber) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (e.target.element.className.indexOf('comment-thread') >= 0) {
|
|
211
|
+
this.toggleExpand(lineNumber);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (this._commentThread.collapsibleState === CommentThreadCollapsibleState.Collapsed) {
|
|
216
|
+
this.display({ afterLineNumber: mouseDownInfo.lineNumber, heightInLines: 2 });
|
|
217
|
+
} else {
|
|
218
|
+
this.hide();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
public get owner(): string {
|
|
223
|
+
return this._owner;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
public get commentThread(): CommentThread {
|
|
227
|
+
return this._commentThread;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private getThreadLabel(): string {
|
|
231
|
+
let label: string | undefined;
|
|
232
|
+
label = this._commentThread.label;
|
|
233
|
+
|
|
234
|
+
if (label === undefined) {
|
|
235
|
+
if (this._commentThread.comments && this._commentThread.comments.length) {
|
|
236
|
+
const onlyUnique = (value: Comment, index: number, self: Comment[]) => self.indexOf(value) === index;
|
|
237
|
+
const participantsList = this._commentThread.comments.filter(onlyUnique).map(comment => `@${comment.userName}`).join(', ');
|
|
238
|
+
const resolutionState = this._commentThread.state === CommentThreadState.Resolved ? '(Resolved)' : '(Unresolved)';
|
|
239
|
+
label = `Participants: ${participantsList} ${resolutionState}`;
|
|
240
|
+
} else {
|
|
241
|
+
label = 'Start discussion';
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return label;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
override update(): void {
|
|
249
|
+
if (!this.isExpanded) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
this.render();
|
|
253
|
+
const headHeight = Math.ceil(this.zoneWidget.editor.getOption(monaco.editor.EditorOption.lineHeight) * 1.2);
|
|
254
|
+
const lineHeight = this.zoneWidget.editor.getOption(monaco.editor.EditorOption.lineHeight);
|
|
255
|
+
const arrowHeight = Math.round(lineHeight / 3);
|
|
256
|
+
const frameThickness = Math.round(lineHeight / 9) * 2;
|
|
257
|
+
const body = this.zoneWidget.containerNode.getElementsByClassName('body')[0];
|
|
258
|
+
|
|
259
|
+
const computedLinesNumber = Math.ceil((headHeight + body.clientHeight + arrowHeight + frameThickness + 8 /** margin bottom to avoid margin collapse */) / lineHeight);
|
|
260
|
+
this.zoneWidget.show({ afterLineNumber: this._commentThread.range.startLineNumber, heightInLines: computedLinesNumber });
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
protected render(): void {
|
|
264
|
+
const headHeight = Math.ceil(this.zoneWidget.editor.getOption(monaco.editor.EditorOption.lineHeight) * 1.2);
|
|
265
|
+
this.containerNodeRoot.render(<div className={'review-widget'}>
|
|
266
|
+
<div className={'head'} style={{ height: headHeight, lineHeight: `${headHeight}px` }}>
|
|
267
|
+
<div className={'review-title'}>
|
|
268
|
+
<span className={'filename'}>{this.getThreadLabel()}</span>
|
|
269
|
+
</div>
|
|
270
|
+
<div className={'review-actions'}>
|
|
271
|
+
<div className={'monaco-action-bar animated'}>
|
|
272
|
+
<ul className={'actions-container'} role={'toolbar'}>
|
|
273
|
+
<li className={'action-item'} role={'presentation'}>
|
|
274
|
+
<a className={'action-label codicon expand-review-action codicon-chevron-up'}
|
|
275
|
+
role={'button'}
|
|
276
|
+
tabIndex={0}
|
|
277
|
+
title={'Collapse'}
|
|
278
|
+
onClick={() => this.collapse()}
|
|
279
|
+
/>
|
|
280
|
+
</li>
|
|
281
|
+
</ul>
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
</div>
|
|
285
|
+
<div className={'body'}>
|
|
286
|
+
<div className={'comments-container'} role={'presentation'} tabIndex={0}>
|
|
287
|
+
{this._commentThread.comments?.map((comment, index) => <ReviewComment
|
|
288
|
+
key={index}
|
|
289
|
+
contextKeyService={this.contextKeyService}
|
|
290
|
+
menus={this.menus}
|
|
291
|
+
comment={comment}
|
|
292
|
+
commentForm={this.commentFormRef}
|
|
293
|
+
commands={this.commands}
|
|
294
|
+
commentThread={this._commentThread}
|
|
295
|
+
/>)}
|
|
296
|
+
</div>
|
|
297
|
+
<CommentForm contextKeyService={this.contextKeyService}
|
|
298
|
+
commands={this.commands}
|
|
299
|
+
commentThread={this._commentThread}
|
|
300
|
+
menus={this.menus}
|
|
301
|
+
widget={this}
|
|
302
|
+
ref={this.commentFormRef}
|
|
303
|
+
/>
|
|
304
|
+
</div>
|
|
305
|
+
</div>);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
namespace CommentForm {
|
|
310
|
+
export interface Props {
|
|
311
|
+
menus: MenuModelRegistry,
|
|
312
|
+
commentThread: CommentThread;
|
|
313
|
+
commands: CommandRegistry;
|
|
314
|
+
contextKeyService: CommentsContextKeyService;
|
|
315
|
+
widget: CommentThreadWidget;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export interface State {
|
|
319
|
+
expanded: boolean
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export class CommentForm<P extends CommentForm.Props = CommentForm.Props> extends React.Component<P, CommentForm.State> {
|
|
324
|
+
private readonly menu: CompoundMenuNode;
|
|
325
|
+
private inputRef: RefObject<HTMLTextAreaElement> = React.createRef<HTMLTextAreaElement>();
|
|
326
|
+
private inputValue: string = '';
|
|
327
|
+
private readonly getInput = () => this.inputValue;
|
|
328
|
+
private readonly clearInput: () => void = () => {
|
|
329
|
+
const input = this.inputRef.current;
|
|
330
|
+
if (input) {
|
|
331
|
+
this.inputValue = '';
|
|
332
|
+
input.value = this.inputValue;
|
|
333
|
+
this.props.contextKeyService.commentIsEmpty.set(true);
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
update(): void {
|
|
338
|
+
this.setState(this.state);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
protected expand = () => {
|
|
342
|
+
this.setState({ expanded: true });
|
|
343
|
+
// Wait for the widget to be rendered.
|
|
344
|
+
setTimeout(() => {
|
|
345
|
+
// Update the widget's height.
|
|
346
|
+
this.props.widget.update();
|
|
347
|
+
this.inputRef.current?.focus();
|
|
348
|
+
}, 100);
|
|
349
|
+
};
|
|
350
|
+
protected collapse = () => {
|
|
351
|
+
this.setState({ expanded: false });
|
|
352
|
+
// Wait for the widget to be rendered.
|
|
353
|
+
setTimeout(() => {
|
|
354
|
+
// Update the widget's height.
|
|
355
|
+
this.props.widget.update();
|
|
356
|
+
}, 100);
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
override componentDidMount(): void {
|
|
360
|
+
// Wait for the widget to be rendered.
|
|
361
|
+
setTimeout(() => {
|
|
362
|
+
this.inputRef.current?.focus();
|
|
363
|
+
}, 100);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
private readonly onInput: (event: React.FormEvent) => void = (event: React.FormEvent) => {
|
|
367
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
368
|
+
const value = (event.target as any).value;
|
|
369
|
+
if (this.inputValue.length === 0 || value.length === 0) {
|
|
370
|
+
this.props.contextKeyService.commentIsEmpty.set(value.length === 0);
|
|
371
|
+
}
|
|
372
|
+
this.inputValue = value;
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
constructor(props: P) {
|
|
376
|
+
super(props);
|
|
377
|
+
this.state = {
|
|
378
|
+
expanded: false
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
const setState = this.setState.bind(this);
|
|
382
|
+
this.setState = newState => {
|
|
383
|
+
setState(newState);
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
this.menu = this.props.menus.getMenu(COMMENT_THREAD_CONTEXT);
|
|
387
|
+
this.menu.children.map(node => node instanceof ActionMenuNode && node.when).forEach(exp => {
|
|
388
|
+
if (typeof exp === 'string') {
|
|
389
|
+
this.props.contextKeyService.setExpression(exp);
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
override render(): React.ReactNode {
|
|
395
|
+
const { commands, commentThread, contextKeyService } = this.props;
|
|
396
|
+
const hasExistingComments = commentThread.comments && commentThread.comments.length > 0;
|
|
397
|
+
return commentThread.canReply ? <div className={'comment-form' + (this.state.expanded || commentThread.comments && commentThread.comments.length === 0 ? ' expand' : '')}>
|
|
398
|
+
<div className={'theia-comments-input-message-container'}>
|
|
399
|
+
<textarea className={'theia-comments-input-message theia-input'}
|
|
400
|
+
spellCheck={false}
|
|
401
|
+
placeholder={hasExistingComments ? 'Reply...' : 'Type a new comment'}
|
|
402
|
+
onInput={this.onInput}
|
|
403
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
404
|
+
onBlur={(event: any) => {
|
|
405
|
+
if (event.target.value.length > 0) {
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
if (event.relatedTarget && event.relatedTarget.className === 'comments-button comments-text-button theia-button') {
|
|
409
|
+
this.state = { expanded: false };
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
this.collapse();
|
|
413
|
+
}}
|
|
414
|
+
ref={this.inputRef}>
|
|
415
|
+
</textarea>
|
|
416
|
+
</div>
|
|
417
|
+
<CommentActions menu={this.menu}
|
|
418
|
+
contextKeyService={contextKeyService}
|
|
419
|
+
commands={commands}
|
|
420
|
+
commentThread={commentThread}
|
|
421
|
+
getInput={this.getInput}
|
|
422
|
+
clearInput={this.clearInput}
|
|
423
|
+
/>
|
|
424
|
+
<button className={'review-thread-reply-button'} title={'Reply...'} onClick={this.expand}>Reply...</button>
|
|
425
|
+
</div> : null;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
namespace ReviewComment {
|
|
430
|
+
export interface Props {
|
|
431
|
+
menus: MenuModelRegistry,
|
|
432
|
+
comment: Comment;
|
|
433
|
+
commentThread: CommentThread;
|
|
434
|
+
contextKeyService: CommentsContextKeyService;
|
|
435
|
+
commands: CommandRegistry;
|
|
436
|
+
commentForm: RefObject<CommentForm>;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
export interface State {
|
|
440
|
+
hover: boolean
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
export class ReviewComment<P extends ReviewComment.Props = ReviewComment.Props> extends React.Component<P, ReviewComment.State> {
|
|
445
|
+
|
|
446
|
+
constructor(props: P) {
|
|
447
|
+
super(props);
|
|
448
|
+
this.state = {
|
|
449
|
+
hover: false
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
const setState = this.setState.bind(this);
|
|
453
|
+
this.setState = newState => {
|
|
454
|
+
setState(newState);
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
protected detectHover = (element: HTMLElement | null) => {
|
|
459
|
+
if (element) {
|
|
460
|
+
window.requestAnimationFrame(() => {
|
|
461
|
+
const hover = element.matches(':hover');
|
|
462
|
+
this.setState({ hover });
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
protected showHover = () => this.setState({ hover: true });
|
|
468
|
+
protected hideHover = () => this.setState({ hover: false });
|
|
469
|
+
|
|
470
|
+
override render(): React.ReactNode {
|
|
471
|
+
const { comment, commentForm, contextKeyService, menus, commands, commentThread } = this.props;
|
|
472
|
+
const commentUniqueId = comment.uniqueIdInThread;
|
|
473
|
+
const { hover } = this.state;
|
|
474
|
+
contextKeyService.comment.set(comment.contextValue);
|
|
475
|
+
return <div className={'review-comment'}
|
|
476
|
+
tabIndex={-1}
|
|
477
|
+
aria-label={`${comment.userName}, ${comment.body.value}`}
|
|
478
|
+
ref={this.detectHover}
|
|
479
|
+
onMouseEnter={this.showHover}
|
|
480
|
+
onMouseLeave={this.hideHover}>
|
|
481
|
+
<div className={'avatar-container'}>
|
|
482
|
+
<img className={'avatar'} src={comment.userIconPath} />
|
|
483
|
+
</div>
|
|
484
|
+
<div className={'review-comment-contents'}>
|
|
485
|
+
<div className={'comment-title monaco-mouse-cursor-text'}>
|
|
486
|
+
<strong className={'author'}>{comment.userName}</strong>
|
|
487
|
+
<small className={'timestamp'}>{this.localeDate(comment.timestamp)}</small>
|
|
488
|
+
<span className={'isPending'}>{comment.label}</span>
|
|
489
|
+
<div className={'theia-comments-inline-actions-container'}>
|
|
490
|
+
<div className={'theia-comments-inline-actions'} role={'toolbar'}>
|
|
491
|
+
{hover && menus.getMenu(COMMENT_TITLE).children.map((node, index) => node instanceof ActionMenuNode &&
|
|
492
|
+
<CommentsInlineAction key={index} {...{ node, commands, commentThread, commentUniqueId, contextKeyService }} />)}
|
|
493
|
+
</div>
|
|
494
|
+
</div>
|
|
495
|
+
</div>
|
|
496
|
+
<CommentBody value={comment.body.value}
|
|
497
|
+
isVisible={comment.mode === undefined || comment.mode === CommentMode.Preview} />
|
|
498
|
+
<CommentEditContainer contextKeyService={contextKeyService}
|
|
499
|
+
menus={menus}
|
|
500
|
+
comment={comment}
|
|
501
|
+
commentThread={commentThread}
|
|
502
|
+
commentForm={commentForm}
|
|
503
|
+
commands={commands} />
|
|
504
|
+
</div>
|
|
505
|
+
</div>;
|
|
506
|
+
}
|
|
507
|
+
protected localeDate(timestamp: string | undefined): string {
|
|
508
|
+
if (timestamp === undefined) {
|
|
509
|
+
return '';
|
|
510
|
+
}
|
|
511
|
+
const date = new Date(timestamp);
|
|
512
|
+
if (!isNaN(date.getTime())) {
|
|
513
|
+
return date.toLocaleString();
|
|
514
|
+
}
|
|
515
|
+
return '';
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
namespace CommentBody {
|
|
520
|
+
export interface Props {
|
|
521
|
+
value: string
|
|
522
|
+
isVisible: boolean
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export class CommentBody extends React.Component<CommentBody.Props> {
|
|
527
|
+
override render(): React.ReactNode {
|
|
528
|
+
const { value, isVisible } = this.props;
|
|
529
|
+
if (!isVisible) {
|
|
530
|
+
return false;
|
|
531
|
+
}
|
|
532
|
+
return <div className={'comment-body monaco-mouse-cursor-text'}>
|
|
533
|
+
<div>
|
|
534
|
+
<p>{value}</p>
|
|
535
|
+
</div>
|
|
536
|
+
</div>;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
namespace CommentEditContainer {
|
|
541
|
+
export interface Props {
|
|
542
|
+
contextKeyService: CommentsContextKeyService
|
|
543
|
+
menus: MenuModelRegistry,
|
|
544
|
+
comment: Comment;
|
|
545
|
+
commentThread: CommentThread;
|
|
546
|
+
commentForm: RefObject<CommentForm>;
|
|
547
|
+
commands: CommandRegistry;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
export class CommentEditContainer extends React.Component<CommentEditContainer.Props> {
|
|
552
|
+
private readonly inputRef: RefObject<HTMLTextAreaElement> = React.createRef<HTMLTextAreaElement>();
|
|
553
|
+
private dirtyCommentMode: CommentMode | undefined;
|
|
554
|
+
private dirtyCommentFormState: boolean | undefined;
|
|
555
|
+
|
|
556
|
+
override componentDidUpdate(prevProps: Readonly<CommentEditContainer.Props>, prevState: Readonly<{}>): void {
|
|
557
|
+
const commentFormState = this.props.commentForm.current?.state;
|
|
558
|
+
const mode = this.props.comment.mode;
|
|
559
|
+
if (this.dirtyCommentMode !== mode || (this.dirtyCommentFormState !== commentFormState?.expanded && !commentFormState?.expanded)) {
|
|
560
|
+
const currentInput = this.inputRef.current;
|
|
561
|
+
if (currentInput) {
|
|
562
|
+
// Wait for the widget to be rendered.
|
|
563
|
+
setTimeout(() => {
|
|
564
|
+
currentInput.focus();
|
|
565
|
+
currentInput.setSelectionRange(currentInput.value.length, currentInput.value.length);
|
|
566
|
+
}, 50);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
this.dirtyCommentMode = mode;
|
|
570
|
+
this.dirtyCommentFormState = commentFormState?.expanded;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
override render(): React.ReactNode {
|
|
574
|
+
const { menus, comment, commands, commentThread, contextKeyService } = this.props;
|
|
575
|
+
if (!(comment.mode === CommentMode.Editing)) {
|
|
576
|
+
return false;
|
|
577
|
+
}
|
|
578
|
+
return <div className={'edit-container'}>
|
|
579
|
+
<div className={'edit-textarea'}>
|
|
580
|
+
<div className={'theia-comments-input-message-container'}>
|
|
581
|
+
<textarea className={'theia-comments-input-message theia-input'}
|
|
582
|
+
spellCheck={false}
|
|
583
|
+
defaultValue={comment.body.value}
|
|
584
|
+
ref={this.inputRef} />
|
|
585
|
+
</div>
|
|
586
|
+
</div>
|
|
587
|
+
<div className={'form-actions'}>
|
|
588
|
+
{menus.getMenu(COMMENT_CONTEXT).children.map((node, index) => {
|
|
589
|
+
const onClick = () => {
|
|
590
|
+
commands.executeCommand(node.id, {
|
|
591
|
+
thread: commentThread,
|
|
592
|
+
commentUniqueId: comment.uniqueIdInThread,
|
|
593
|
+
text: this.inputRef.current ? this.inputRef.current.value : ''
|
|
594
|
+
});
|
|
595
|
+
};
|
|
596
|
+
return node instanceof ActionMenuNode &&
|
|
597
|
+
<CommentAction key={index} {...{ node, commands, onClick, contextKeyService }} />;
|
|
598
|
+
}
|
|
599
|
+
)}
|
|
600
|
+
</div>
|
|
601
|
+
</div>;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
namespace CommentsInlineAction {
|
|
606
|
+
export interface Props {
|
|
607
|
+
node: ActionMenuNode;
|
|
608
|
+
commentThread: CommentThread;
|
|
609
|
+
commentUniqueId: number;
|
|
610
|
+
commands: CommandRegistry;
|
|
611
|
+
contextKeyService: CommentsContextKeyService;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
export class CommentsInlineAction extends React.Component<CommentsInlineAction.Props> {
|
|
616
|
+
override render(): React.ReactNode {
|
|
617
|
+
const { node, commands, contextKeyService, commentThread, commentUniqueId } = this.props;
|
|
618
|
+
if (node.when && !contextKeyService.match(node.when)) {
|
|
619
|
+
return false;
|
|
620
|
+
}
|
|
621
|
+
return <div className='theia-comments-inline-action'>
|
|
622
|
+
<a className={node.icon}
|
|
623
|
+
title={node.label}
|
|
624
|
+
onClick={() => {
|
|
625
|
+
commands.executeCommand(node.id, {
|
|
626
|
+
thread: commentThread,
|
|
627
|
+
commentUniqueId
|
|
628
|
+
});
|
|
629
|
+
}} />
|
|
630
|
+
</div>;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
namespace CommentActions {
|
|
635
|
+
export interface Props {
|
|
636
|
+
contextKeyService: CommentsContextKeyService;
|
|
637
|
+
commands: CommandRegistry;
|
|
638
|
+
menu: CompoundMenuNode;
|
|
639
|
+
commentThread: CommentThread;
|
|
640
|
+
getInput: () => string;
|
|
641
|
+
clearInput: () => void;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
export class CommentActions extends React.Component<CommentActions.Props> {
|
|
646
|
+
override render(): React.ReactNode {
|
|
647
|
+
const { contextKeyService, commands, menu, commentThread, getInput, clearInput } = this.props;
|
|
648
|
+
return <div className={'form-actions'}>
|
|
649
|
+
{menu.children.map((node, index) => node instanceof ActionMenuNode &&
|
|
650
|
+
<CommentAction key={index}
|
|
651
|
+
commands={commands}
|
|
652
|
+
node={node}
|
|
653
|
+
onClick={() => {
|
|
654
|
+
commands.executeCommand(node.id, {
|
|
655
|
+
thread: commentThread,
|
|
656
|
+
text: getInput()
|
|
657
|
+
});
|
|
658
|
+
clearInput();
|
|
659
|
+
}}
|
|
660
|
+
contextKeyService={contextKeyService}
|
|
661
|
+
/>)}
|
|
662
|
+
</div>;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
namespace CommentAction {
|
|
666
|
+
export interface Props {
|
|
667
|
+
contextKeyService: CommentsContextKeyService;
|
|
668
|
+
commands: CommandRegistry;
|
|
669
|
+
node: ActionMenuNode;
|
|
670
|
+
onClick: () => void;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
export class CommentAction extends React.Component<CommentAction.Props> {
|
|
675
|
+
override render(): React.ReactNode {
|
|
676
|
+
const classNames = ['comments-button', 'comments-text-button', 'theia-button'];
|
|
677
|
+
const { node, commands, contextKeyService, onClick } = this.props;
|
|
678
|
+
if (node.when && !contextKeyService.match(node.when)) {
|
|
679
|
+
return false;
|
|
680
|
+
}
|
|
681
|
+
const isEnabled = commands.isEnabled(node.command);
|
|
682
|
+
if (!isEnabled) {
|
|
683
|
+
classNames.push(DISABLED_CLASS);
|
|
684
|
+
}
|
|
685
|
+
return <button
|
|
686
|
+
className={classNames.join(' ')}
|
|
687
|
+
tabIndex={0}
|
|
688
|
+
role={'button'}
|
|
689
|
+
onClick={() => {
|
|
690
|
+
if (isEnabled) {
|
|
691
|
+
onClick();
|
|
692
|
+
}
|
|
693
|
+
}}>{node.label}
|
|
694
|
+
</button>;
|
|
695
|
+
}
|
|
696
|
+
}
|