Merge from vscode c873727e8bac95e7cbf5b154a9e6ae0986f2ce18 (#6446)
@@ -3614,9 +3614,9 @@ vsce@1.48.0:
|
||||
yazl "^2.2.2"
|
||||
|
||||
vscode-ripgrep@^1.4.0:
|
||||
version "1.5.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.5.4.tgz#dae1c3eef350513299341cdf96e622c00b548eff"
|
||||
integrity sha512-Bs8SvFAkR0QHf09J46VgNo19yRikOtj/f0zHzK3AM3ICjCGNN/BNoG9of6zGVHUTO+6Mk1RbKglyOHLKr8D1lg==
|
||||
version "1.5.5"
|
||||
resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.5.5.tgz#24c0e9cb356cf889c98e15ecb58f9cf654a1d961"
|
||||
integrity sha512-OrPrAmcun4+uZAuNcQvE6CCPskh+5AsjANod/Q3zRcJcGNxgoOSGlQN9RPtatkUNmkN8Nn8mZBnS1jMylu/dKg==
|
||||
|
||||
vscode-telemetry-extractor@1.4.3:
|
||||
version "1.4.3"
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M13.451 5.609l-.579-.939-1.068.812-.076.094c-.335.415-.927 1.341-1.124 2.876l-.021.165.033.163.071.345c0 1.654-1.346 3-3 3-.795 0-1.545-.311-2.107-.868-.563-.567-.873-1.317-.873-2.111 0-1.431 1.007-2.632 2.351-2.929v2.926s2.528-2.087 2.984-2.461h.012l3.061-2.582-4.919-4.1h-1.137v2.404c-3.429.318-6.121 3.211-6.121 6.721 0 1.809.707 3.508 1.986 4.782 1.277 1.282 2.976 1.988 4.784 1.988 3.722 0 6.75-3.028 6.75-6.75 0-1.245-.349-2.468-1.007-3.536z" fill="#2D2D30"/><path d="M12.6 6.134l-.094.071c-.269.333-.746 1.096-.91 2.375.057.277.092.495.092.545 0 2.206-1.794 4-4 4-1.098 0-2.093-.445-2.817-1.164-.718-.724-1.163-1.718-1.163-2.815 0-2.206 1.794-4 4-4l.351.025v1.85s1.626-1.342 1.631-1.339l1.869-1.577-3.5-2.917v2.218l-.371-.03c-3.176 0-5.75 2.574-5.75 5.75 0 1.593.648 3.034 1.695 4.076 1.042 1.046 2.482 1.694 4.076 1.694 3.176 0 5.75-2.574 5.75-5.75-.001-1.106-.318-2.135-.859-3.012z" fill="#C5C5C5"/></svg>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.56253 2.51574C3.46348 3.45007 2 5.55411 2 7.99996C2 11.3137 4.68629 14 8 14C11.3137 14 14 11.3137 14 7.99996C14 5.32516 12.2497 3.05916 9.83199 2.28479L9.52968 3.23829C11.5429 3.88451 13 5.77207 13 7.99996C13 10.7614 10.7614 13 8 13C5.23858 13 3 10.7614 3 7.99996C3 6.31101 3.83742 4.81764 5.11969 3.91242L5.56253 2.51574Z" fill="#C5C5C5"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 3H2V2H5.5L6 2.5V6H5V3Z" fill="#C5C5C5"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 986 B After Width: | Height: | Size: 589 B |
@@ -1 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M13.451 5.609l-.579-.939-1.068.812-.076.094c-.335.415-.927 1.341-1.124 2.876l-.021.165.033.163.071.345c0 1.654-1.346 3-3 3-.795 0-1.545-.311-2.107-.868-.563-.567-.873-1.317-.873-2.111 0-1.431 1.007-2.632 2.351-2.929v2.926s2.528-2.087 2.984-2.461h.012l3.061-2.582-4.919-4.1h-1.137v2.404c-3.429.318-6.121 3.211-6.121 6.721 0 1.809.707 3.508 1.986 4.782 1.277 1.282 2.976 1.988 4.784 1.988 3.722 0 6.75-3.028 6.75-6.75 0-1.245-.349-2.468-1.007-3.536z" fill="#F6F6F6"/><path d="M12.6 6.134l-.094.071c-.269.333-.746 1.096-.91 2.375.057.277.092.495.092.545 0 2.206-1.794 4-4 4-1.098 0-2.093-.445-2.817-1.164-.718-.724-1.163-1.718-1.163-2.815 0-2.206 1.794-4 4-4l.351.025v1.85s1.626-1.342 1.631-1.339l1.869-1.577-3.5-2.917v2.218l-.371-.03c-3.176 0-5.75 2.574-5.75 5.75 0 1.593.648 3.034 1.695 4.076 1.042 1.046 2.482 1.694 4.076 1.694 3.176 0 5.75-2.574 5.75-5.75-.001-1.106-.318-2.135-.859-3.012z" fill="#424242"/></svg>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.56253 2.51574C3.46348 3.45007 2 5.55411 2 7.99996C2 11.3137 4.68629 14 8 14C11.3137 14 14 11.3137 14 7.99996C14 5.32516 12.2497 3.05916 9.83199 2.28479L9.52968 3.23829C11.5429 3.88451 13 5.77207 13 7.99996C13 10.7614 10.7614 13 8 13C5.23858 13 3 10.7614 3 7.99996C3 6.31101 3.83742 4.81764 5.11969 3.91242L5.56253 2.51574Z" fill="#424242"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 3H2V2H5.5L6 2.5V6H5V3Z" fill="#424242"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 986 B After Width: | Height: | Size: 589 B |
@@ -63,11 +63,20 @@ function registerVariableCompletions(pattern: string): vscode.Disposable {
|
||||
const indexOf$ = document.lineAt(position.line).text.indexOf('$');
|
||||
const startPosition = indexOf$ >= 0 ? new vscode.Position(position.line, indexOf$) : position;
|
||||
|
||||
return [{ label: 'workspaceFolder', detail: localize('workspaceFolder', "The path of the folder opened in VS Code") }, { label: 'workspaceFolderBasename', detail: localize('workspaceFolderBasename', "The name of the folder opened in VS Code without any slashes (/)") },
|
||||
{ label: 'relativeFile', detail: localize('relativeFile', "The current opened file relative to ${workspaceFolder}") }, { label: 'file', detail: localize('file', "The current opened file") }, { label: 'cwd', detail: localize('cwd', "The task runner's current working directory on startup") },
|
||||
{ label: 'lineNumber', detail: localize('lineNumber', "The current selected line number in the active file") }, { label: 'selectedText', detail: localize('selectedText', "The current selected text in the active file") },
|
||||
{ label: 'fileDirname', detail: localize('fileDirname', "The current opened file's dirname") }, { label: 'fileExtname', detail: localize('fileExtname', "The current opened file's extension") }, { label: 'fileBasename', detail: localize('fileBasename', "The current opened file's basename") },
|
||||
{ label: 'fileBasenameNoExtension', detail: localize('fileBasenameNoExtension', "The current opened file's basename with no file extension") }].map(variable => ({
|
||||
return [
|
||||
{ label: 'workspaceFolder', detail: localize('workspaceFolder', "The path of the folder opened in VS Code") },
|
||||
{ label: 'workspaceFolderBasename', detail: localize('workspaceFolderBasename', "The name of the folder opened in VS Code without any slashes (/)") },
|
||||
{ label: 'relativeFile', detail: localize('relativeFile', "The current opened file relative to ${workspaceFolder}") },
|
||||
{ label: 'relativeFileDirname', detail: localize('relativeFileDirname', "The current opened file's dirname relative to ${workspaceFolder}") },
|
||||
{ label: 'file', detail: localize('file', "The current opened file") },
|
||||
{ label: 'cwd', detail: localize('cwd', "The task runner's current working directory on startup") },
|
||||
{ label: 'lineNumber', detail: localize('lineNumber', "The current selected line number in the active file") },
|
||||
{ label: 'selectedText', detail: localize('selectedText', "The current selected text in the active file") },
|
||||
{ label: 'fileDirname', detail: localize('fileDirname', "The current opened file's dirname") },
|
||||
{ label: 'fileExtname', detail: localize('fileExtname', "The current opened file's extension") },
|
||||
{ label: 'fileBasename', detail: localize('fileBasename', "The current opened file's basename") },
|
||||
{ label: 'fileBasenameNoExtension', detail: localize('fileBasenameNoExtension', "The current opened file's basename with no file extension") }
|
||||
].map(variable => ({
|
||||
label: '${' + variable.label + '}',
|
||||
range: new vscode.Range(startPosition, position),
|
||||
detail: variable.detail
|
||||
|
||||
@@ -21,6 +21,8 @@ The server implements the following capabilities of the language server protocol
|
||||
- [Document Symbols](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentSymbol) for quick navigation to properties in the document.
|
||||
- [Document Colors](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentColor) for showing color decorators on values representing colors and [Color Presentation](https://microsoft.github.io/language-server-protocol/specification#textDocument_colorPresentation) for color presentation information to support color pickers. The location of colors is defined by the document's [JSON schema](http://json-schema.org/). All values marked with `"format": "color-hex"` (VSCode specific, non-standard JSON Schema extension) are considered color values. The supported color formats are `#rgb[a]` and `#rrggbb[aa]`.
|
||||
- [Code Formatting](https://microsoft.github.io/language-server-protocol/specification#textDocument_rangeFormatting) supporting ranges and formatting the whole document.
|
||||
- [Folding Ranges](https://microsoft.github.io/language-server-protocol/specification#textDocument_foldingRange) for all folding ranges in the document.
|
||||
- Semantic Selection for semantic selection for one or multiple cursor positions.
|
||||
- [Diagnostics (Validation)](https://microsoft.github.io/language-server-protocol/specification#textDocument_publishDiagnostics) are pushed for all open documents
|
||||
- syntax errors
|
||||
- structural validation based on the document's [JSON schema](http://json-schema.org/).
|
||||
@@ -90,10 +92,39 @@ To find the schema for a given JSON document, the server uses the following mech
|
||||
- The settings define a schema association based on the documents URL. Settings can either associate a schema URL to a file or path pattern, and they can directly provide a schema.
|
||||
- Additionally, schema associations can also be provided by a custom 'schemaAssociations' configuration call.
|
||||
|
||||
Schemas are identified by URLs. To load the content of a schema, the JSON language server tries to load from that URL or path. The following URL schemas are supported:
|
||||
Schemas are identified by URLs. To load the content of a schema, the JSON language server either tries to load from that URI or path itself, or delegates to the client.
|
||||
|
||||
The `initializationOptions.handledSchemaProtocols` initialization option defines which URLs are handled by the server. Requests for all other URIs are send to the client.
|
||||
|
||||
`handledSchemaProtocols` is part of the initialization options and can't be changed while the server is running.
|
||||
|
||||
```ts
|
||||
let clientOptions: LanguageClientOptions = {
|
||||
initializationOptions: {
|
||||
handledSchemaProtocols: ['file'] // language server should only try to load file URLs
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
If `handledSchemaProtocols` is not set, the JSON language server will load the following URLs itself:
|
||||
|
||||
- `http`, `https`: Loaded using NodeJS's HTTP support. Proxies can be configured through the settings.
|
||||
- `file`: Loaded using NodeJS's `fs` support.
|
||||
- `vscode`: Loaded by an LSP call to the client.
|
||||
|
||||
#### Schema content request
|
||||
|
||||
Requests for schemas with URLs not handled by the server are forwarded to the client through an LSP request. This request is a JSON language server specific, non-standardized, extension to the LSP.
|
||||
|
||||
Request:
|
||||
- method: 'vscode/content'
|
||||
- params: `string` - The schema URL to request.
|
||||
- response: `string` - The content of the schema with the given URL
|
||||
|
||||
#### Schema content change notification
|
||||
|
||||
When the client is aware that a schema content has changed, it will notify the server through a notification. This notification is a JSON language server specific, non-standardized, extension to the LSP.
|
||||
The server will, as a response, clear the schema content from the cache and reload the schema content when required again.
|
||||
|
||||
#### Schema associations notification
|
||||
|
||||
@@ -111,20 +142,6 @@ interface ISchemaAssociations {
|
||||
- keys: a file names or file path (separated by `/`). `*` can be used as a wildcard.
|
||||
- values: An array of schema URLs
|
||||
|
||||
#### Schema content request
|
||||
|
||||
The schema content for schema URLs that start with `vscode://` will be requested from the client through an LSP request. This request is a JSON language server specific, non-standardized, extension to the LSP.
|
||||
|
||||
Request:
|
||||
- method: 'vscode/content'
|
||||
- params: `string` - The schema URL to request. The server will only ask for URLs that start with `vscode://`
|
||||
- response: `string` - The content of the schema with the given URL
|
||||
|
||||
#### Schema content change notification
|
||||
|
||||
When the client is aware that a schema content has changed, it will notify the server through a notification. This notification is a JSON language server specific, non-standardized, extension to the LSP.
|
||||
The server will, as a response, clear the schema content from the cache and reload the schema content when required again.
|
||||
|
||||
Notification:
|
||||
- method: 'json/schemaContent'
|
||||
- params: `string` the URL of the schema that has changed.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "vscode-json-languageserver",
|
||||
"description": "JSON language server",
|
||||
"version": "1.0.1",
|
||||
"version": "1.2.0",
|
||||
"author": "Microsoft Corporation",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
||||
@@ -10,7 +10,7 @@ let fs = require('fs');
|
||||
let https = require('https');
|
||||
let url = require('url');
|
||||
|
||||
// list of languagesIs not shipped with VSCode. The information is used to associate an icon with a langauge association
|
||||
// list of languagesIs not shipped with VSCode. The information is used to associate an icon with a language association
|
||||
let nonBuiltInLanguages = { // { fileNames, extensions }
|
||||
"r": { extensions: ['r', 'rhistory', 'rprofile', 'rt'] },
|
||||
"argdown": { extensions: ['ad', 'adown', 'argdown', 'argdn'] },
|
||||
@@ -35,7 +35,7 @@ let nonBuiltInLanguages = { // { fileNames, extensions }
|
||||
"todo": { fileNames: ['todo'] }
|
||||
};
|
||||
|
||||
let FROM_DISK = false; // set to true to take content from a repo checkedout next to the vscode repo
|
||||
let FROM_DISK = true; // set to true to take content from a repo checked out next to the vscode repo
|
||||
|
||||
let font, fontMappingsFile, fileAssociationFile, colorsFile;
|
||||
if (!FROM_DISK) {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"git": {
|
||||
"name": "seti-ui",
|
||||
"repositoryUrl": "https://github.com/jesseweed/seti-ui",
|
||||
"commitHash": "89175d7f9e0c70cd325b80a18a3c77fc8eb7c798"
|
||||
"commitHash": "904c16acced1134a81b31d71d60293288c31334b"
|
||||
}
|
||||
},
|
||||
"version": "0.1.0"
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
"v8-inspect-profiler": "^0.0.20",
|
||||
"vscode-chokidar": "2.1.7",
|
||||
"vscode-proxy-agent": "0.4.0",
|
||||
"vscode-ripgrep": "^1.5.4",
|
||||
"vscode-ripgrep": "^1.5.5",
|
||||
"vscode-sqlite3": "4.0.8",
|
||||
"vscode-textmate": "^4.2.2",
|
||||
"xterm": "3.15.0-beta71",
|
||||
@@ -192,4 +192,4 @@
|
||||
"windows-mutex": "0.3.0",
|
||||
"windows-process-tree": "0.2.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"spdlog": "^0.9.0",
|
||||
"vscode-chokidar": "2.1.7",
|
||||
"vscode-proxy-agent": "0.4.0",
|
||||
"vscode-ripgrep": "^1.5.4",
|
||||
"vscode-ripgrep": "^1.5.5",
|
||||
"vscode-textmate": "^4.2.2",
|
||||
"xterm": "3.15.0-beta71",
|
||||
"xterm-addon-search": "0.2.0-beta2",
|
||||
|
||||
@@ -1123,10 +1123,10 @@ vscode-proxy-agent@0.4.0:
|
||||
https-proxy-agent "2.2.1"
|
||||
socks-proxy-agent "4.0.1"
|
||||
|
||||
vscode-ripgrep@^1.5.4:
|
||||
version "1.5.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.5.4.tgz#dae1c3eef350513299341cdf96e622c00b548eff"
|
||||
integrity sha512-Bs8SvFAkR0QHf09J46VgNo19yRikOtj/f0zHzK3AM3ICjCGNN/BNoG9of6zGVHUTO+6Mk1RbKglyOHLKr8D1lg==
|
||||
vscode-ripgrep@^1.5.5:
|
||||
version "1.5.5"
|
||||
resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.5.5.tgz#24c0e9cb356cf889c98e15ecb58f9cf654a1d961"
|
||||
integrity sha512-OrPrAmcun4+uZAuNcQvE6CCPskh+5AsjANod/Q3zRcJcGNxgoOSGlQN9RPtatkUNmkN8Nn8mZBnS1jMylu/dKg==
|
||||
|
||||
vscode-textmate@^4.2.2:
|
||||
version "4.2.2"
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-action-green{fill:#388a34;}</style></defs><title>continue</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M14.334,8,3.667,16H3V0h.667Z"/></g><g id="iconBg"><path class="icon-vs-action-green" d="M4,1.5v13L12.667,8,4,1.5Z"/></g></svg>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 2V14.4805L12.9146 8.24024L4 2ZM11.1809 8.24024L4.995 12.5684V3.91209L11.1809 8.24024Z" fill="#C5C5C5"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 505 B After Width: | Height: | Size: 259 B |
@@ -1 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#252526;}.icon-canvas-transparent{opacity:0;}.icon-vs-action-green{fill:#89d185;}</style></defs><title>continue</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M14.334,8,3.667,16H3V0h.667Z"/></g><g id="iconBg"><path class="icon-vs-action-green" d="M4,1.5v13L12.667,8,4,1.5Z"/></g></svg>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 2V14.4805L12.9146 8.24024L4 2ZM11.1809 8.24024L4.995 12.5684V3.91209L11.1809 8.24024Z" fill="#424242"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 505 B After Width: | Height: | Size: 259 B |
@@ -22,10 +22,12 @@ export interface ICheckboxOpts extends ICheckboxStyles {
|
||||
|
||||
export interface ICheckboxStyles {
|
||||
inputActiveOptionBorder?: Color;
|
||||
inputActiveOptionBackground?: Color;
|
||||
}
|
||||
|
||||
const defaultOpts = {
|
||||
inputActiveOptionBorder: Color.fromHex('#007ACC')
|
||||
inputActiveOptionBorder: Color.fromHex('#007ACC00'),
|
||||
inputActiveOptionBackground: Color.fromHex('#0E639C50')
|
||||
};
|
||||
|
||||
export class CheckboxActionViewItem extends BaseActionViewItem {
|
||||
@@ -149,12 +151,16 @@ export class Checkbox extends Widget {
|
||||
if (styles.inputActiveOptionBorder) {
|
||||
this._opts.inputActiveOptionBorder = styles.inputActiveOptionBorder;
|
||||
}
|
||||
if (styles.inputActiveOptionBackground) {
|
||||
this._opts.inputActiveOptionBackground = styles.inputActiveOptionBackground;
|
||||
}
|
||||
this.applyStyles();
|
||||
}
|
||||
|
||||
protected applyStyles(): void {
|
||||
if (this.domNode) {
|
||||
this.domNode.style.borderColor = this._checked && this._opts.inputActiveOptionBorder ? this._opts.inputActiveOptionBorder.toString() : 'transparent';
|
||||
this.domNode.style.backgroundColor = this._checked && this._opts.inputActiveOptionBackground ? this._opts.inputActiveOptionBackground.toString() : 'transparent';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ export interface IFindInputOptions extends IFindInputStyles {
|
||||
|
||||
export interface IFindInputStyles extends IInputBoxStyles {
|
||||
inputActiveOptionBorder?: Color;
|
||||
inputActiveOptionBackground?: Color;
|
||||
}
|
||||
|
||||
const NLS_DEFAULT_LABEL = nls.localize('defaultLabel', "input");
|
||||
@@ -48,6 +49,7 @@ export class FindInput extends Widget {
|
||||
private fixFocusOnOptionClickEnabled = true;
|
||||
|
||||
private inputActiveOptionBorder?: Color;
|
||||
private inputActiveOptionBackground?: Color;
|
||||
private inputBackground?: Color;
|
||||
private inputForeground?: Color;
|
||||
private inputBorder?: Color;
|
||||
@@ -97,6 +99,7 @@ export class FindInput extends Widget {
|
||||
this.label = options.label || NLS_DEFAULT_LABEL;
|
||||
|
||||
this.inputActiveOptionBorder = options.inputActiveOptionBorder;
|
||||
this.inputActiveOptionBackground = options.inputActiveOptionBackground;
|
||||
this.inputBackground = options.inputBackground;
|
||||
this.inputForeground = options.inputForeground;
|
||||
this.inputBorder = options.inputBorder;
|
||||
@@ -173,6 +176,7 @@ export class FindInput extends Widget {
|
||||
|
||||
public style(styles: IFindInputStyles): void {
|
||||
this.inputActiveOptionBorder = styles.inputActiveOptionBorder;
|
||||
this.inputActiveOptionBackground = styles.inputActiveOptionBackground;
|
||||
this.inputBackground = styles.inputBackground;
|
||||
this.inputForeground = styles.inputForeground;
|
||||
this.inputBorder = styles.inputBorder;
|
||||
@@ -194,6 +198,7 @@ export class FindInput extends Widget {
|
||||
if (this.domNode) {
|
||||
const checkBoxStyles: ICheckboxStyles = {
|
||||
inputActiveOptionBorder: this.inputActiveOptionBorder,
|
||||
inputActiveOptionBackground: this.inputActiveOptionBackground,
|
||||
};
|
||||
this.regex.style(checkBoxStyles);
|
||||
this.wholeWords.style(checkBoxStyles);
|
||||
@@ -294,7 +299,8 @@ export class FindInput extends Widget {
|
||||
this.regex = this._register(new RegexCheckbox({
|
||||
appendTitle: appendRegexLabel,
|
||||
isChecked: false,
|
||||
inputActiveOptionBorder: this.inputActiveOptionBorder
|
||||
inputActiveOptionBorder: this.inputActiveOptionBorder,
|
||||
inputActiveOptionBackground: this.inputActiveOptionBackground
|
||||
}));
|
||||
this._register(this.regex.onChange(viaKeyboard => {
|
||||
this._onDidOptionChange.fire(viaKeyboard);
|
||||
@@ -310,7 +316,8 @@ export class FindInput extends Widget {
|
||||
this.wholeWords = this._register(new WholeWordsCheckbox({
|
||||
appendTitle: appendWholeWordsLabel,
|
||||
isChecked: false,
|
||||
inputActiveOptionBorder: this.inputActiveOptionBorder
|
||||
inputActiveOptionBorder: this.inputActiveOptionBorder,
|
||||
inputActiveOptionBackground: this.inputActiveOptionBackground
|
||||
}));
|
||||
this._register(this.wholeWords.onChange(viaKeyboard => {
|
||||
this._onDidOptionChange.fire(viaKeyboard);
|
||||
@@ -323,7 +330,8 @@ export class FindInput extends Widget {
|
||||
this.caseSensitive = this._register(new CaseSensitiveCheckbox({
|
||||
appendTitle: appendCaseSensitiveLabel,
|
||||
isChecked: false,
|
||||
inputActiveOptionBorder: this.inputActiveOptionBorder
|
||||
inputActiveOptionBorder: this.inputActiveOptionBorder,
|
||||
inputActiveOptionBackground: this.inputActiveOptionBackground
|
||||
}));
|
||||
this._register(this.caseSensitive.onChange(viaKeyboard => {
|
||||
this._onDidOptionChange.fire(viaKeyboard);
|
||||
|
||||
@@ -12,6 +12,7 @@ export interface IFindInputCheckboxOpts {
|
||||
readonly appendTitle: string;
|
||||
readonly isChecked: boolean;
|
||||
readonly inputActiveOptionBorder?: Color;
|
||||
readonly inputActiveOptionBackground?: Color;
|
||||
}
|
||||
|
||||
const NLS_CASE_SENSITIVE_CHECKBOX_LABEL = nls.localize('caseDescription', "Match Case");
|
||||
@@ -24,7 +25,8 @@ export class CaseSensitiveCheckbox extends Checkbox {
|
||||
actionClassName: 'monaco-case-sensitive',
|
||||
title: NLS_CASE_SENSITIVE_CHECKBOX_LABEL + opts.appendTitle,
|
||||
isChecked: opts.isChecked,
|
||||
inputActiveOptionBorder: opts.inputActiveOptionBorder
|
||||
inputActiveOptionBorder: opts.inputActiveOptionBorder,
|
||||
inputActiveOptionBackground: opts.inputActiveOptionBackground
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -35,7 +37,8 @@ export class WholeWordsCheckbox extends Checkbox {
|
||||
actionClassName: 'monaco-whole-word',
|
||||
title: NLS_WHOLE_WORD_CHECKBOX_LABEL + opts.appendTitle,
|
||||
isChecked: opts.isChecked,
|
||||
inputActiveOptionBorder: opts.inputActiveOptionBorder
|
||||
inputActiveOptionBorder: opts.inputActiveOptionBorder,
|
||||
inputActiveOptionBackground: opts.inputActiveOptionBackground
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -46,7 +49,8 @@ export class RegexCheckbox extends Checkbox {
|
||||
actionClassName: 'monaco-regex',
|
||||
title: NLS_REGEX_CHECKBOX_LABEL + opts.appendTitle,
|
||||
isChecked: opts.isChecked,
|
||||
inputActiveOptionBorder: opts.inputActiveOptionBorder
|
||||
inputActiveOptionBorder: opts.inputActiveOptionBorder,
|
||||
inputActiveOptionBackground: opts.inputActiveOptionBackground
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,6 +207,43 @@ export class MutableDisposable<T extends IDisposable> implements IDisposable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class that stores a disposable that is not currently "owned" by anyone.
|
||||
*
|
||||
* Example use cases:
|
||||
*
|
||||
* - Express that a function/method will take ownership of a disposable parameter.
|
||||
* - Express that a function returns a disposable that the caller must explicitly take ownership of.
|
||||
*/
|
||||
export class UnownedDisposable<T extends IDisposable> extends Disposable {
|
||||
private _hasBeenAcquired = false;
|
||||
private _value?: T;
|
||||
|
||||
public constructor(value: T) {
|
||||
super();
|
||||
this._value = value;
|
||||
}
|
||||
|
||||
public acquire(): T {
|
||||
if (this._hasBeenAcquired) {
|
||||
throw new Error('This disposable has already been acquired');
|
||||
}
|
||||
this._hasBeenAcquired = true;
|
||||
const value = this._value!;
|
||||
this._value = undefined;
|
||||
return value;
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
super.dispose();
|
||||
if (!this._hasBeenAcquired) {
|
||||
this._hasBeenAcquired = true;
|
||||
this._value!.dispose();
|
||||
this._value = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface IReference<T> extends IDisposable {
|
||||
readonly object: T;
|
||||
}
|
||||
|
||||
@@ -460,7 +460,7 @@ const editorConfiguration: IConfigurationNode = {
|
||||
'editor.fastScrollSensitivity': {
|
||||
'type': 'number',
|
||||
'default': EDITOR_DEFAULTS.viewInfo.scrollbar.fastScrollSensitivity,
|
||||
'markdownDescription': nls.localize('fastScrollSensitivity', "Scrolling speed mulitiplier when pressing `Alt`.")
|
||||
'markdownDescription': nls.localize('fastScrollSensitivity', "Scrolling speed multiplier when pressing `Alt`.")
|
||||
},
|
||||
'editor.multiCursorModifier': {
|
||||
'type': 'string',
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { RunOnceScheduler, TimeoutTimer } from 'vs/base/common/async';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { dispose, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ReplaceCommand, ReplaceCommandThatPreservesSelection } from 'vs/editor/common/commands/replaceCommand';
|
||||
import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
|
||||
@@ -71,7 +71,7 @@ export class FindModelBoundToEditorModel {
|
||||
|
||||
private readonly _editor: IActiveCodeEditor;
|
||||
private readonly _state: FindReplaceState;
|
||||
private _toDispose: IDisposable[];
|
||||
private readonly _toDispose = new DisposableStore();
|
||||
private readonly _decorations: FindDecorations;
|
||||
private _ignoreModelContentChanged: boolean;
|
||||
private readonly _startSearchingTimer: TimeoutTimer;
|
||||
@@ -82,17 +82,16 @@ export class FindModelBoundToEditorModel {
|
||||
constructor(editor: IActiveCodeEditor, state: FindReplaceState) {
|
||||
this._editor = editor;
|
||||
this._state = state;
|
||||
this._toDispose = [];
|
||||
this._isDisposed = false;
|
||||
this._startSearchingTimer = new TimeoutTimer();
|
||||
|
||||
this._decorations = new FindDecorations(editor);
|
||||
this._toDispose.push(this._decorations);
|
||||
this._toDispose.add(this._decorations);
|
||||
|
||||
this._updateDecorationsScheduler = new RunOnceScheduler(() => this.research(false), 100);
|
||||
this._toDispose.push(this._updateDecorationsScheduler);
|
||||
this._toDispose.add(this._updateDecorationsScheduler);
|
||||
|
||||
this._toDispose.push(this._editor.onDidChangeCursorPosition((e: ICursorPositionChangedEvent) => {
|
||||
this._toDispose.add(this._editor.onDidChangeCursorPosition((e: ICursorPositionChangedEvent) => {
|
||||
if (
|
||||
e.reason === CursorChangeReason.Explicit
|
||||
|| e.reason === CursorChangeReason.Undo
|
||||
@@ -103,7 +102,7 @@ export class FindModelBoundToEditorModel {
|
||||
}));
|
||||
|
||||
this._ignoreModelContentChanged = false;
|
||||
this._toDispose.push(this._editor.onDidChangeModelContent((e) => {
|
||||
this._toDispose.add(this._editor.onDidChangeModelContent((e) => {
|
||||
if (this._ignoreModelContentChanged) {
|
||||
return;
|
||||
}
|
||||
@@ -115,7 +114,7 @@ export class FindModelBoundToEditorModel {
|
||||
this._updateDecorationsScheduler.schedule();
|
||||
}));
|
||||
|
||||
this._toDispose.push(this._state.onFindReplaceStateChange((e) => this._onStateChanged(e)));
|
||||
this._toDispose.add(this._state.onFindReplaceStateChange((e) => this._onStateChanged(e)));
|
||||
|
||||
this.research(false, this._state.searchScope);
|
||||
}
|
||||
@@ -123,7 +122,7 @@ export class FindModelBoundToEditorModel {
|
||||
public dispose(): void {
|
||||
this._isDisposed = true;
|
||||
dispose(this._startSearchingTimer);
|
||||
this._toDispose = dispose(this._toDispose);
|
||||
this._toDispose.dispose();
|
||||
}
|
||||
|
||||
private _onStateChanged(e: FindReplaceStateChangedEvent): void {
|
||||
|
||||
@@ -11,7 +11,7 @@ import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPosit
|
||||
import { FIND_IDS } from 'vs/editor/contrib/find/findModel';
|
||||
import { FindReplaceState } from 'vs/editor/contrib/find/findState';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { contrastBorder, editorWidgetBackground, inputActiveOptionBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { contrastBorder, editorWidgetBackground, inputActiveOptionBorder, inputActiveOptionBackground, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
export class FindOptionsWidget extends Widget implements IOverlayWidget {
|
||||
@@ -47,11 +47,13 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget {
|
||||
this._domNode.setAttribute('aria-hidden', 'true');
|
||||
|
||||
const inputActiveOptionBorderColor = themeService.getTheme().getColor(inputActiveOptionBorder);
|
||||
const inputActiveOptionBackgroundColor = themeService.getTheme().getColor(inputActiveOptionBackground);
|
||||
|
||||
this.caseSensitive = this._register(new CaseSensitiveCheckbox({
|
||||
appendTitle: this._keybindingLabelFor(FIND_IDS.ToggleCaseSensitiveCommand),
|
||||
isChecked: this._state.matchCase,
|
||||
inputActiveOptionBorder: inputActiveOptionBorderColor
|
||||
inputActiveOptionBorder: inputActiveOptionBorderColor,
|
||||
inputActiveOptionBackground: inputActiveOptionBackgroundColor
|
||||
}));
|
||||
this._domNode.appendChild(this.caseSensitive.domNode);
|
||||
this._register(this.caseSensitive.onChange(() => {
|
||||
@@ -63,7 +65,8 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget {
|
||||
this.wholeWords = this._register(new WholeWordsCheckbox({
|
||||
appendTitle: this._keybindingLabelFor(FIND_IDS.ToggleWholeWordCommand),
|
||||
isChecked: this._state.wholeWord,
|
||||
inputActiveOptionBorder: inputActiveOptionBorderColor
|
||||
inputActiveOptionBorder: inputActiveOptionBorderColor,
|
||||
inputActiveOptionBackground: inputActiveOptionBackgroundColor
|
||||
}));
|
||||
this._domNode.appendChild(this.wholeWords.domNode);
|
||||
this._register(this.wholeWords.onChange(() => {
|
||||
@@ -75,7 +78,8 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget {
|
||||
this.regex = this._register(new RegexCheckbox({
|
||||
appendTitle: this._keybindingLabelFor(FIND_IDS.ToggleRegexCommand),
|
||||
isChecked: this._state.isRegex,
|
||||
inputActiveOptionBorder: inputActiveOptionBorderColor
|
||||
inputActiveOptionBorder: inputActiveOptionBorderColor,
|
||||
inputActiveOptionBackground: inputActiveOptionBackgroundColor
|
||||
}));
|
||||
this._domNode.appendChild(this.regex.domNode);
|
||||
this._register(this.regex.onChange(() => {
|
||||
@@ -179,7 +183,10 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget {
|
||||
}
|
||||
|
||||
private _applyTheme(theme: ITheme) {
|
||||
let inputStyles = { inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder) };
|
||||
let inputStyles = {
|
||||
inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder),
|
||||
inputActiveOptionBackground: theme.getColor(inputActiveOptionBackground)
|
||||
};
|
||||
this.caseSensitive.style(inputStyles);
|
||||
this.wholeWords.style(inputStyles);
|
||||
this.regex.style(inputStyles);
|
||||
|
||||
@@ -289,10 +289,6 @@
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.monaco-editor.vs-dark .find-widget .monaco-checkbox .checkbox:checked + .label {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.monaco-editor.hc-black .find-widget .close-fw,
|
||||
.monaco-editor.vs-dark .find-widget .close-fw {
|
||||
background-image: url('images/close-dark.svg');
|
||||
|
||||
@@ -27,7 +27,7 @@ import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_REPLACE_INPUT_FOCUSED, FIND_IDS, MA
|
||||
import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/findState';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { contrastBorder, editorFindMatch, editorFindMatchBorder, editorFindMatchHighlight, editorFindMatchHighlightBorder, editorFindRangeHighlight, editorFindRangeHighlightBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetResizeBorder, errorForeground, inputActiveOptionBorder, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { contrastBorder, editorFindMatch, editorFindMatchBorder, editorFindMatchHighlight, editorFindMatchHighlightBorder, editorFindRangeHighlight, editorFindRangeHighlightBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetResizeBorder, errorForeground, inputActiveOptionBorder, inputActiveOptionBackground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { ContextScopedFindInput, ContextScopedHistoryInputBox } from 'vs/platform/browser/contextScopedHistoryWidget';
|
||||
import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
|
||||
@@ -560,6 +560,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
|
||||
private _applyTheme(theme: ITheme) {
|
||||
let inputStyles: IFindInputStyles = {
|
||||
inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder),
|
||||
inputActiveOptionBackground: theme.getColor(inputActiveOptionBackground),
|
||||
inputBackground: theme.getColor(inputBackground),
|
||||
inputForeground: theme.getColor(inputForeground),
|
||||
inputBorder: theme.getColor(inputBorder),
|
||||
@@ -1217,4 +1218,9 @@ registerThemingParticipant((theme, collector) => {
|
||||
if (inputActiveBorder) {
|
||||
collector.addRule(`.monaco-editor .find-widget .monaco-checkbox .checkbox:checked + .label { border: 1px solid ${inputActiveBorder.toString()}; }`);
|
||||
}
|
||||
|
||||
const inputActiveBackground = theme.getColor(inputActiveOptionBackground);
|
||||
if (inputActiveBackground) {
|
||||
collector.addRule(`.monaco-editor .find-widget .monaco-checkbox .checkbox:checked + .label { background-color: ${inputActiveBackground.toString()}; }`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@ import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBo
|
||||
import { SimpleButton } from 'vs/editor/contrib/find/findWidget';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { editorWidgetBackground, inputActiveOptionBorder, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { editorWidgetBackground, inputActiveOptionBorder, inputActiveOptionBackground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ITheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { ContextScopedFindInput } from 'vs/platform/browser/contextScopedHistoryWidget';
|
||||
|
||||
@@ -180,6 +180,7 @@ export abstract class SimpleFindWidget extends Widget {
|
||||
public updateTheme(theme: ITheme): void {
|
||||
const inputStyles: IFindInputStyles = {
|
||||
inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder),
|
||||
inputActiveOptionBackground: theme.getColor(inputActiveOptionBackground),
|
||||
inputBackground: theme.getColor(inputBackground),
|
||||
inputForeground: theme.getColor(inputForeground),
|
||||
inputBorder: theme.getColor(inputBorder),
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as nls from 'vs/nls';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IMarker, IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/markers';
|
||||
@@ -32,7 +32,7 @@ class MarkerModel {
|
||||
private readonly _editor: ICodeEditor;
|
||||
private _markers: IMarker[];
|
||||
private _nextIdx: number;
|
||||
private _toUnbind: IDisposable[];
|
||||
private readonly _toUnbind = new DisposableStore();
|
||||
private _ignoreSelectionChange: boolean;
|
||||
private readonly _onCurrentMarkerChanged: Emitter<IMarker | undefined>;
|
||||
private readonly _onMarkerSetChanged: Emitter<MarkerModel>;
|
||||
@@ -41,15 +41,14 @@ class MarkerModel {
|
||||
this._editor = editor;
|
||||
this._markers = [];
|
||||
this._nextIdx = -1;
|
||||
this._toUnbind = [];
|
||||
this._ignoreSelectionChange = false;
|
||||
this._onCurrentMarkerChanged = new Emitter<IMarker>();
|
||||
this._onMarkerSetChanged = new Emitter<MarkerModel>();
|
||||
this.setMarkers(markers);
|
||||
|
||||
// listen on editor
|
||||
this._toUnbind.push(this._editor.onDidDispose(() => this.dispose()));
|
||||
this._toUnbind.push(this._editor.onDidChangeCursorPosition(() => {
|
||||
this._toUnbind.add(this._editor.onDidDispose(() => this.dispose()));
|
||||
this._toUnbind.add(this._editor.onDidChangeCursorPosition(() => {
|
||||
if (this._ignoreSelectionChange) {
|
||||
return;
|
||||
}
|
||||
@@ -190,7 +189,7 @@ class MarkerModel {
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._toUnbind = dispose(this._toUnbind);
|
||||
this._toUnbind.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -520,10 +520,14 @@ export class DiagnosticsService implements IDiagnosticsService {
|
||||
if (folderUri.scheme === 'file') {
|
||||
const folder = folderUri.fsPath;
|
||||
collectWorkspaceStats(folder, ['node_modules', '.git']).then(stats => {
|
||||
type WorkspaceStatItemClassification = {
|
||||
name: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
|
||||
count: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
|
||||
};
|
||||
type WorkspaceStatsClassification = {
|
||||
fileTypes: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
|
||||
configTypes: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
|
||||
launchConfigs: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
|
||||
fileTypes: WorkspaceStatItemClassification;
|
||||
configTypes: WorkspaceStatItemClassification;
|
||||
launchConfigs: WorkspaceStatItemClassification;
|
||||
};
|
||||
type WorkspaceStatsEvent = {
|
||||
fileTypes: WorkspaceStatItem[];
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ResolvedAuthority, IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { ResolvedAuthority, IRemoteAuthorityResolverService, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
|
||||
export class RemoteAuthorityResolverService implements IRemoteAuthorityResolverService {
|
||||
|
||||
@@ -12,12 +12,16 @@ export class RemoteAuthorityResolverService implements IRemoteAuthorityResolverS
|
||||
constructor() {
|
||||
}
|
||||
|
||||
resolveAuthority(authority: string): Promise<ResolvedAuthority> {
|
||||
resolveAuthority(authority: string): Promise<ResolverResult> {
|
||||
if (authority.indexOf(':') >= 0) {
|
||||
const pieces = authority.split(':');
|
||||
return Promise.resolve({ authority, host: pieces[0], port: parseInt(pieces[1], 10) });
|
||||
return Promise.resolve({
|
||||
authority: { authority, host: pieces[0], port: parseInt(pieces[1], 10) }
|
||||
});
|
||||
}
|
||||
return Promise.resolve({ authority, host: authority, port: 80 });
|
||||
return Promise.resolve({
|
||||
authority: { authority, host: authority, port: 80 }
|
||||
});
|
||||
}
|
||||
|
||||
clearResolvedAuthority(authority: string): void {
|
||||
|
||||
@@ -163,6 +163,7 @@ export interface IRemoteExtensionHostStartParams {
|
||||
debugId?: string;
|
||||
break?: boolean;
|
||||
port?: number | null;
|
||||
env?: { [key: string]: string | null };
|
||||
}
|
||||
|
||||
interface IExtensionHostConnectionResult {
|
||||
|
||||
@@ -13,6 +13,15 @@ export interface ResolvedAuthority {
|
||||
readonly port: number;
|
||||
}
|
||||
|
||||
export interface ResolvedOptions {
|
||||
readonly extensionHostEnv?: { [key: string]: string | null };
|
||||
}
|
||||
|
||||
export interface ResolverResult {
|
||||
authority: ResolvedAuthority;
|
||||
options?: ResolvedOptions;
|
||||
}
|
||||
|
||||
export enum RemoteAuthorityResolverErrorCode {
|
||||
Unknown = 'Unknown',
|
||||
NotAvailable = 'NotAvailable',
|
||||
@@ -61,9 +70,9 @@ export interface IRemoteAuthorityResolverService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
resolveAuthority(authority: string): Promise<ResolvedAuthority>;
|
||||
resolveAuthority(authority: string): Promise<ResolverResult>;
|
||||
|
||||
clearResolvedAuthority(authority: string): void;
|
||||
setResolvedAuthority(resolvedAuthority: ResolvedAuthority): void;
|
||||
setResolvedAuthority(resolvedAuthority: ResolvedAuthority, resolvedOptions?: ResolvedOptions): void;
|
||||
setResolvedAuthorityError(authority: string, err: any): void;
|
||||
}
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ResolvedAuthority, IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { ResolvedAuthority, IRemoteAuthorityResolverService, ResolverResult, ResolvedOptions } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { ipcRenderer as ipc } from 'electron';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
|
||||
class PendingResolveAuthorityRequest {
|
||||
constructor(
|
||||
public readonly resolve: (value: ResolvedAuthority) => void,
|
||||
public readonly resolve: (value: ResolverResult) => void,
|
||||
public readonly reject: (err: any) => void,
|
||||
public readonly promise: Promise<ResolvedAuthority>,
|
||||
public readonly promise: Promise<ResolverResult>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -26,11 +26,11 @@ export class RemoteAuthorityResolverService implements IRemoteAuthorityResolverS
|
||||
this._resolveAuthorityRequests = Object.create(null);
|
||||
}
|
||||
|
||||
resolveAuthority(authority: string): Promise<ResolvedAuthority> {
|
||||
resolveAuthority(authority: string): Promise<ResolverResult> {
|
||||
if (!this._resolveAuthorityRequests[authority]) {
|
||||
let resolve: (value: ResolvedAuthority) => void;
|
||||
let resolve: (value: ResolverResult) => void;
|
||||
let reject: (err: any) => void;
|
||||
let promise = new Promise<ResolvedAuthority>((_resolve, _reject) => {
|
||||
let promise = new Promise<ResolverResult>((_resolve, _reject) => {
|
||||
resolve = _resolve;
|
||||
reject = _reject;
|
||||
});
|
||||
@@ -46,11 +46,11 @@ export class RemoteAuthorityResolverService implements IRemoteAuthorityResolverS
|
||||
}
|
||||
}
|
||||
|
||||
setResolvedAuthority(resolvedAuthority: ResolvedAuthority) {
|
||||
setResolvedAuthority(resolvedAuthority: ResolvedAuthority, options?: ResolvedOptions) {
|
||||
if (this._resolveAuthorityRequests[resolvedAuthority.authority]) {
|
||||
let request = this._resolveAuthorityRequests[resolvedAuthority.authority];
|
||||
ipc.send('vscode:remoteAuthorityResolved', resolvedAuthority);
|
||||
request.resolve(resolvedAuthority);
|
||||
request.resolve({ authority: resolvedAuthority, options });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import { ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils'
|
||||
import { optional } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { cloneAndChange, mixin } from 'vs/base/common/objects';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings';
|
||||
@@ -35,7 +35,7 @@ export class TelemetryService implements ITelemetryService {
|
||||
private _userOptIn: boolean;
|
||||
private _enabled: boolean;
|
||||
|
||||
private _disposables: IDisposable[] = [];
|
||||
private readonly _disposables = new DisposableStore();
|
||||
private _cleanupPatterns: RegExp[] = [];
|
||||
|
||||
constructor(
|
||||
@@ -113,7 +113,7 @@ export class TelemetryService implements ITelemetryService {
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposables = dispose(this._disposables);
|
||||
this._disposables.dispose();
|
||||
}
|
||||
|
||||
publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): Promise<any> {
|
||||
|
||||
@@ -205,7 +205,8 @@ export const widgetShadow = registerColor('widget.shadow', { dark: '#000000', li
|
||||
export const inputBackground = registerColor('input.background', { dark: '#3C3C3C', light: Color.white, hc: Color.black }, nls.localize('inputBoxBackground', "Input box background."));
|
||||
export const inputForeground = registerColor('input.foreground', { dark: foreground, light: foreground, hc: foreground }, nls.localize('inputBoxForeground', "Input box foreground."));
|
||||
export const inputBorder = registerColor('input.border', { dark: null, light: null, hc: contrastBorder }, nls.localize('inputBoxBorder', "Input box border."));
|
||||
export const inputActiveOptionBorder = registerColor('inputOption.activeBorder', { dark: '#007ACC', light: '#007ACC', hc: activeContrastBorder }, nls.localize('inputBoxActiveOptionBorder', "Border color of activated options in input fields."));
|
||||
export const inputActiveOptionBorder = registerColor('inputOption.activeBorder', { dark: '#007ACC00', light: '#007ACC00', hc: contrastBorder }, nls.localize('inputBoxActiveOptionBorder', "Border color of activated options in input fields."));
|
||||
export const inputActiveOptionBackground = registerColor('inputOption.activeBackground', { dark: transparent(focusBorder, 0.5), light: transparent(focusBorder, 0.3), hc: null }, nls.localize('inputOption.activeBackground', "Background color of activated options in input fields."));
|
||||
export const inputPlaceholderForeground = registerColor('input.placeholderForeground', { light: transparent(foreground, 0.5), dark: transparent(foreground, 0.5), hc: transparent(foreground, 0.7) }, nls.localize('inputPlaceholderForeground', "Input box foreground color for placeholder text."));
|
||||
|
||||
export const inputValidationInfoBackground = registerColor('inputValidation.infoBackground', { dark: '#063B49', light: '#D6ECF2', hc: Color.black }, nls.localize('inputValidationInfoBackground', "Input validation background color for information severity."));
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, darken, listFilterWidgetOutline, listFilterWidgetNoMatchesOutline, listFilterWidgetBackground, editorWidgetBackground, treeIndentGuidesStroke, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, darken, listFilterWidgetOutline, listFilterWidgetNoMatchesOutline, listFilterWidgetBackground, editorWidgetBackground, treeIndentGuidesStroke, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
@@ -59,11 +59,13 @@ export function attachStyler<T extends IColorMapping>(themeService: IThemeServic
|
||||
|
||||
export interface ICheckboxStyleOverrides extends IStyleOverrides {
|
||||
inputActiveOptionBorderColor?: ColorIdentifier;
|
||||
inputActiveOptionBackgroundColor?: ColorIdentifier;
|
||||
}
|
||||
|
||||
export function attachCheckboxStyler(widget: IThemable, themeService: IThemeService, style?: ICheckboxStyleOverrides): IDisposable {
|
||||
return attachStyler(themeService, {
|
||||
inputActiveOptionBorder: (style && style.inputActiveOptionBorderColor) || inputActiveOptionBorder
|
||||
inputActiveOptionBorder: (style && style.inputActiveOptionBorderColor) || inputActiveOptionBorder,
|
||||
inputActiveOptionBackground: (style && style.inputActiveOptionBackgroundColor) || inputActiveOptionBackground
|
||||
} as ICheckboxStyleOverrides, widget);
|
||||
}
|
||||
|
||||
@@ -85,6 +87,7 @@ export interface IInputBoxStyleOverrides extends IStyleOverrides {
|
||||
inputForeground?: ColorIdentifier;
|
||||
inputBorder?: ColorIdentifier;
|
||||
inputActiveOptionBorder?: ColorIdentifier;
|
||||
inputActiveOptionBackground?: ColorIdentifier;
|
||||
inputValidationInfoBorder?: ColorIdentifier;
|
||||
inputValidationInfoBackground?: ColorIdentifier;
|
||||
inputValidationInfoForeground?: ColorIdentifier;
|
||||
@@ -146,6 +149,7 @@ export function attachFindInputBoxStyler(widget: IThemable, themeService: ITheme
|
||||
inputForeground: (style && style.inputForeground) || inputForeground,
|
||||
inputBorder: (style && style.inputBorder) || inputBorder,
|
||||
inputActiveOptionBorder: (style && style.inputActiveOptionBorder) || inputActiveOptionBorder,
|
||||
inputActiveOptionBackground: (style && style.inputActiveOptionBackground) || inputActiveOptionBackground,
|
||||
inputValidationInfoBorder: (style && style.inputValidationInfoBorder) || inputValidationInfoBorder,
|
||||
inputValidationInfoBackground: (style && style.inputValidationInfoBackground) || inputValidationInfoBackground,
|
||||
inputValidationInfoForeground: (style && style.inputValidationInfoForeground) || inputValidationInfoForeground,
|
||||
|
||||
8
src/vs/vscode.proposed.d.ts
vendored
@@ -94,6 +94,12 @@ declare module 'vscode' {
|
||||
constructor(host: string, port: number);
|
||||
}
|
||||
|
||||
export interface ResolvedOptions {
|
||||
extensionHostEnv?: { [key: string]: string | null };
|
||||
}
|
||||
|
||||
export type ResolverResult = ResolvedAuthority & ResolvedOptions;
|
||||
|
||||
export class RemoteAuthorityResolverError extends Error {
|
||||
static NotAvailable(message?: string, handled?: boolean): RemoteAuthorityResolverError;
|
||||
static TemporarilyNotAvailable(message?: string): RemoteAuthorityResolverError;
|
||||
@@ -102,7 +108,7 @@ declare module 'vscode' {
|
||||
}
|
||||
|
||||
export interface RemoteAuthorityResolver {
|
||||
resolve(authority: string, context: RemoteAuthorityResolverContext): ResolvedAuthority | Thenable<ResolvedAuthority>;
|
||||
resolve(authority: string, context: RemoteAuthorityResolverContext): ResolverResult | Thenable<ResolverResult>;
|
||||
}
|
||||
|
||||
export interface ResourceLabelFormatter {
|
||||
|
||||
@@ -31,7 +31,7 @@ import { LogLevel } from 'vs/platform/log/common/log';
|
||||
import { IMarkerData } from 'vs/platform/markers/common/markers';
|
||||
import { IProgressOptions, IProgressStep } from 'vs/platform/progress/common/progress';
|
||||
import * as quickInput from 'vs/platform/quickinput/common/quickInput';
|
||||
import { RemoteAuthorityResolverErrorCode, ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { RemoteAuthorityResolverErrorCode, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import * as statusbar from 'vs/platform/statusbar/common/statusbar';
|
||||
import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings';
|
||||
import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
|
||||
@@ -859,7 +859,7 @@ export interface IResolveAuthorityErrorResult {
|
||||
|
||||
export interface IResolveAuthorityOKResult {
|
||||
type: 'ok';
|
||||
value: ResolvedAuthority;
|
||||
value: ResolverResult;
|
||||
}
|
||||
|
||||
export type IResolveAuthorityResult = IResolveAuthorityErrorResult | IResolveAuthorityOKResult;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { debounce } from 'vs/base/common/decorators';
|
||||
import { dispose, IDisposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
|
||||
import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
|
||||
import { asPromise } from 'vs/base/common/async';
|
||||
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceSplice, SCMRawResourceSplices, IMainContext, ExtHostSCMShape, CommandDto } from './extHost.protocol';
|
||||
@@ -263,7 +263,6 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG
|
||||
}
|
||||
|
||||
readonly handle = ExtHostSourceControlResourceGroup._handlePool++;
|
||||
private _disposables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
private _proxy: MainThreadSCMShape,
|
||||
@@ -353,7 +352,6 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG
|
||||
|
||||
dispose(): void {
|
||||
this._proxy.$unregisterGroup(this._sourceControlHandle, this.handle);
|
||||
this._disposables = dispose(this._disposables);
|
||||
this._onDidDispose.fire();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import { ExtensionMemento } from 'vs/workbench/api/common/extHostMemento';
|
||||
import { ExtensionStoragePaths } from 'vs/workbench/api/node/extHostStoragePaths';
|
||||
import { RemoteAuthorityResolverError, ExtensionExecutionContext } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IURITransformer } from 'vs/base/common/uriIpc';
|
||||
import { ResolvedAuthority, ResolvedOptions } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
|
||||
interface ITestRunner {
|
||||
/** Old test runner API, as exported from `vscode/lib/testrunner` */
|
||||
@@ -680,12 +681,22 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
|
||||
|
||||
try {
|
||||
const result = await resolver.resolve(remoteAuthority, { resolveAttempt });
|
||||
|
||||
// Split merged API result into separate authority/options
|
||||
const authority: ResolvedAuthority = {
|
||||
authority: remoteAuthority,
|
||||
host: result.host,
|
||||
port: result.port
|
||||
};
|
||||
const options: ResolvedOptions = {
|
||||
extensionHostEnv: result.extensionHostEnv
|
||||
};
|
||||
|
||||
return {
|
||||
type: 'ok',
|
||||
value: {
|
||||
authority: remoteAuthority,
|
||||
host: result.host,
|
||||
port: result.port,
|
||||
authority,
|
||||
options
|
||||
}
|
||||
};
|
||||
} catch (err) {
|
||||
|
||||
@@ -20,10 +20,10 @@ export class CommentsDataSource implements IDataSource {
|
||||
return 'root';
|
||||
}
|
||||
if (element instanceof ResourceWithCommentThreads) {
|
||||
return element.id;
|
||||
return `${element.owner}-${element.id}`;
|
||||
}
|
||||
if (element instanceof CommentNode) {
|
||||
return `${element.resource.toString()}-${element.comment.uniqueIdInThread}`;
|
||||
return `${element.owner}-${element.resource.toString()}-${element.threadId}-${element.comment.uniqueIdInThread}`;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -15,13 +15,15 @@ export interface ICommentThreadChangedEvent extends CommentThreadChangedEvent {
|
||||
}
|
||||
|
||||
export class CommentNode {
|
||||
owner: string;
|
||||
threadId: string;
|
||||
range: IRange;
|
||||
comment: Comment;
|
||||
replies: CommentNode[] = [];
|
||||
resource: URI;
|
||||
|
||||
constructor(threadId: string, resource: URI, comment: Comment, range: IRange) {
|
||||
constructor(owner: string, threadId: string, resource: URI, comment: Comment, range: IRange) {
|
||||
this.owner = owner;
|
||||
this.threadId = threadId;
|
||||
this.comment = comment;
|
||||
this.resource = resource;
|
||||
@@ -35,18 +37,20 @@ export class CommentNode {
|
||||
|
||||
export class ResourceWithCommentThreads {
|
||||
id: string;
|
||||
owner: string;
|
||||
commentThreads: CommentNode[]; // The top level comments on the file. Replys are nested under each node.
|
||||
resource: URI;
|
||||
|
||||
constructor(resource: URI, commentThreads: CommentThread[]) {
|
||||
constructor(owner: string, resource: URI, commentThreads: CommentThread[]) {
|
||||
this.owner = owner;
|
||||
this.id = resource.toString();
|
||||
this.resource = resource;
|
||||
this.commentThreads = commentThreads.filter(thread => thread.comments && thread.comments.length).map(thread => ResourceWithCommentThreads.createCommentNode(resource, thread));
|
||||
this.commentThreads = commentThreads.filter(thread => thread.comments && thread.comments.length).map(thread => ResourceWithCommentThreads.createCommentNode(owner, resource, thread));
|
||||
}
|
||||
|
||||
public static createCommentNode(resource: URI, commentThread: CommentThread): CommentNode {
|
||||
public static createCommentNode(owner: string, resource: URI, commentThread: CommentThread): CommentNode {
|
||||
const { threadId, comments, range } = commentThread;
|
||||
const commentNodes: CommentNode[] = comments!.map(comment => new CommentNode(threadId!, resource, comment, range));
|
||||
const commentNodes: CommentNode[] = comments!.map(comment => new CommentNode(owner, threadId!, resource, comment, range));
|
||||
if (commentNodes.length > 1) {
|
||||
commentNodes[0].replies = commentNodes.slice(1, commentNodes.length);
|
||||
}
|
||||
@@ -65,7 +69,7 @@ export class CommentsModel {
|
||||
}
|
||||
|
||||
public setCommentThreads(owner: string, commentThreads: CommentThread[]): void {
|
||||
this.commentThreadsMap.set(owner, this.groupByResource(commentThreads));
|
||||
this.commentThreadsMap.set(owner, this.groupByResource(owner, commentThreads));
|
||||
this.resourceCommentThreads = flatten(values(this.commentThreadsMap));
|
||||
}
|
||||
|
||||
@@ -97,9 +101,9 @@ export class CommentsModel {
|
||||
// Find comment node on resource that is that thread and replace it
|
||||
const index = firstIndex(matchingResourceData.commentThreads, (commentThread) => commentThread.threadId === thread.threadId);
|
||||
if (index >= 0) {
|
||||
matchingResourceData.commentThreads[index] = ResourceWithCommentThreads.createCommentNode(URI.parse(matchingResourceData.id), thread);
|
||||
matchingResourceData.commentThreads[index] = ResourceWithCommentThreads.createCommentNode(owner, URI.parse(matchingResourceData.id), thread);
|
||||
} else if (thread.comments && thread.comments.length) {
|
||||
matchingResourceData.commentThreads.push(ResourceWithCommentThreads.createCommentNode(URI.parse(matchingResourceData.id), thread));
|
||||
matchingResourceData.commentThreads.push(ResourceWithCommentThreads.createCommentNode(owner, URI.parse(matchingResourceData.id), thread));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -108,10 +112,10 @@ export class CommentsModel {
|
||||
if (existingResource.length) {
|
||||
const resource = existingResource[0];
|
||||
if (thread.comments && thread.comments.length) {
|
||||
resource.commentThreads.push(ResourceWithCommentThreads.createCommentNode(resource.resource, thread));
|
||||
resource.commentThreads.push(ResourceWithCommentThreads.createCommentNode(owner, resource.resource, thread));
|
||||
}
|
||||
} else {
|
||||
threadsForOwner.push(new ResourceWithCommentThreads(URI.parse(thread.resource!), [thread]));
|
||||
threadsForOwner.push(new ResourceWithCommentThreads(owner, URI.parse(thread.resource!), [thread]));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -133,11 +137,11 @@ export class CommentsModel {
|
||||
}
|
||||
}
|
||||
|
||||
private groupByResource(commentThreads: CommentThread[]): ResourceWithCommentThreads[] {
|
||||
private groupByResource(owner: string, commentThreads: CommentThread[]): ResourceWithCommentThreads[] {
|
||||
const resourceCommentThreads: ResourceWithCommentThreads[] = [];
|
||||
const commentThreadsByResource = new Map<string, ResourceWithCommentThreads>();
|
||||
for (const group of groupBy(commentThreads, CommentsModel._compareURIs)) {
|
||||
commentThreadsByResource.set(group[0].resource!, new ResourceWithCommentThreads(URI.parse(group[0].resource!), group));
|
||||
commentThreadsByResource.set(group[0].resource!, new ResourceWithCommentThreads(owner, URI.parse(group[0].resource!), group));
|
||||
}
|
||||
|
||||
commentThreadsByResource.forEach((v, i, m) => {
|
||||
|
||||
@@ -10,17 +10,17 @@ import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { language } from 'vs/base/common/platform';
|
||||
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { match } from 'vs/base/common/glob';
|
||||
import { IRequestService, asJson } from 'vs/platform/request/common/request';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { ITextFileService, StateChange } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { WorkspaceStats } from 'vs/workbench/contrib/stats/electron-browser/workspaceStats';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExperimentState, IExperimentAction, IExperimentService, IExperiment, ExperimentActionType, IExperimentActionPromptProperties } from 'vs/workbench/contrib/experiments/common/experimentService';
|
||||
import { IProductService } from 'vs/platform/product/common/product';
|
||||
import { IWorkspaceStatsService } from 'vs/workbench/contrib/stats/electron-browser/workspaceStatsService';
|
||||
|
||||
interface IExperimentStorageState {
|
||||
enabled: boolean;
|
||||
@@ -60,7 +60,6 @@ export class ExperimentService extends Disposable implements IExperimentService
|
||||
private _experiments: IExperiment[] = [];
|
||||
private _loadExperimentsPromise: Promise<void>;
|
||||
private _curatedMapping = Object.create(null);
|
||||
private _disposables: IDisposable[] = [];
|
||||
|
||||
private readonly _onExperimentEnabled = this._register(new Emitter<IExperiment>());
|
||||
onExperimentEnabled: Event<IExperiment> = this._onExperimentEnabled.event;
|
||||
@@ -74,7 +73,8 @@ export class ExperimentService extends Disposable implements IExperimentService
|
||||
@ILifecycleService private readonly lifecycleService: ILifecycleService,
|
||||
@IRequestService private readonly requestService: IRequestService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IProductService private readonly productService: IProductService
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@IWorkspaceStatsService private readonly workspaceStatsService: IWorkspaceStatsService
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -355,7 +355,7 @@ export class ExperimentService extends Disposable implements IExperimentService
|
||||
onSaveHandler.dispose();
|
||||
return;
|
||||
}
|
||||
e.forEach(event => {
|
||||
e.forEach(async event => {
|
||||
if (event.kind !== StateChange.SAVED
|
||||
|| latestExperimentState.state !== ExperimentState.Evaluating
|
||||
|| date === latestExperimentState.lastEditedDate
|
||||
@@ -370,10 +370,12 @@ export class ExperimentService extends Disposable implements IExperimentService
|
||||
filePathCheck = match(fileEdits.filePathPattern, event.resource.fsPath);
|
||||
}
|
||||
if (Array.isArray(fileEdits.workspaceIncludes) && fileEdits.workspaceIncludes.length) {
|
||||
workspaceCheck = !!WorkspaceStats.TAGS && fileEdits.workspaceIncludes.some(x => !!WorkspaceStats.TAGS[x]);
|
||||
const tags = await this.workspaceStatsService.getTags();
|
||||
workspaceCheck = !!tags && fileEdits.workspaceIncludes.some(x => !!tags[x]);
|
||||
}
|
||||
if (workspaceCheck && Array.isArray(fileEdits.workspaceExcludes) && fileEdits.workspaceExcludes.length) {
|
||||
workspaceCheck = !!WorkspaceStats.TAGS && !fileEdits.workspaceExcludes.some(x => !!WorkspaceStats.TAGS[x]);
|
||||
const tags = await this.workspaceStatsService.getTags();
|
||||
workspaceCheck = !!tags && !fileEdits.workspaceExcludes.some(x => !!tags[x]);
|
||||
}
|
||||
if (filePathCheck && workspaceCheck) {
|
||||
latestExperimentState.editCount = (latestExperimentState.editCount || 0) + 1;
|
||||
@@ -389,14 +391,10 @@ export class ExperimentService extends Disposable implements IExperimentService
|
||||
}
|
||||
}
|
||||
});
|
||||
this._disposables.push(onSaveHandler);
|
||||
this._register(onSaveHandler);
|
||||
return ExperimentState.Evaluating;
|
||||
});
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._disposables = dispose(this._disposables);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import { dispose, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewlet, AutoUpdateConfigurationKey, IExtensionContainer, EXTENSIONS_CONFIG, ExtensionsPolicy, ExtensionsPolicyKey } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||
import { ExtensionsConfigurationInitialContent } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate';
|
||||
import { ExtensionsLabel, IGalleryExtension, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionTipsService, IExtensionRecommendation, IExtensionsConfigContent } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
import { IExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionTipsService, IExtensionRecommendation, IExtensionsConfigContent, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { ExtensionType, ExtensionIdentifier, IExtensionDescription, IExtensionManifest, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -176,19 +176,22 @@ export class InstallAction extends ExtensionAction {
|
||||
}
|
||||
|
||||
update(): void {
|
||||
if (!this.extension || this.extension.type === ExtensionType.System || this.extension.state === ExtensionState.Installed) {
|
||||
this.enabled = false;
|
||||
this.class = InstallAction.Class;
|
||||
this.label = InstallAction.INSTALL_LABEL;
|
||||
return;
|
||||
}
|
||||
this.enabled = false;
|
||||
if (this.extensionsWorkbenchService.canInstall(this.extension)) {
|
||||
const local = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, this.extension.identifier))[0];
|
||||
this.enabled = !local || (!!local.local && isLanguagePackExtension(local.local.manifest));
|
||||
this.class = InstallAction.Class;
|
||||
this.label = InstallAction.INSTALL_LABEL;
|
||||
if (this.extension && this.extension.type === ExtensionType.User) {
|
||||
if (this.extension.state === ExtensionState.Uninstalled && this.extensionsWorkbenchService.canInstall(this.extension)) {
|
||||
this.enabled = true;
|
||||
this.updateLabel();
|
||||
return;
|
||||
}
|
||||
if (this.extension.state === ExtensionState.Installing) {
|
||||
this.enabled = false;
|
||||
this.updateLabel();
|
||||
this.class = this.extension.state === ExtensionState.Installing ? InstallAction.InstallingClass : InstallAction.Class;
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.class = this.extension.state === ExtensionState.Installing ? InstallAction.InstallingClass : InstallAction.Class;
|
||||
this.updateLabel();
|
||||
}
|
||||
|
||||
private updateLabel(): void {
|
||||
@@ -283,60 +286,55 @@ export class InstallAction extends ExtensionAction {
|
||||
}
|
||||
}
|
||||
|
||||
export class RemoteInstallAction extends ExtensionAction {
|
||||
export class InstallInOtherServerAction extends ExtensionAction {
|
||||
|
||||
private static INSTALL_LABEL = localize('install', "Install");
|
||||
private static INSTALLING_LABEL = localize('installing', "Installing");
|
||||
protected static INSTALL_LABEL = localize('install', "Install");
|
||||
protected static INSTALLING_LABEL = localize('installing', "Installing");
|
||||
|
||||
private static readonly Class = 'extension-action prominent install';
|
||||
private static readonly InstallingClass = 'extension-action install installing';
|
||||
|
||||
updateWhenCounterExtensionChanges: boolean = true;
|
||||
private installing: boolean = false;
|
||||
protected installing: boolean = false;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
private readonly server: IExtensionManagementServer | null,
|
||||
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@ILabelService private readonly labelService: ILabelService,
|
||||
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
) {
|
||||
super(`extensions.remoteinstall`, RemoteInstallAction.INSTALL_LABEL, RemoteInstallAction.Class, false);
|
||||
this._register(this.labelService.onDidChangeFormatters(() => this.updateLabel(), this));
|
||||
super(id, InstallInOtherServerAction.INSTALL_LABEL, InstallInOtherServerAction.Class, false);
|
||||
this.updateLabel();
|
||||
this.update();
|
||||
}
|
||||
|
||||
private updateLabel(): void {
|
||||
if (this.installing) {
|
||||
this.label = RemoteInstallAction.INSTALLING_LABEL;
|
||||
this.tooltip = this.label;
|
||||
return;
|
||||
}
|
||||
const remoteServer = this.extensionManagementServerService.remoteExtensionManagementServer;
|
||||
if (remoteServer) {
|
||||
this.label = `${RemoteInstallAction.INSTALL_LABEL} on ${remoteServer.label}`;
|
||||
this.tooltip = this.label;
|
||||
return;
|
||||
}
|
||||
this.label = this.getLabel();
|
||||
this.tooltip = this.label;
|
||||
}
|
||||
|
||||
protected getLabel(): string {
|
||||
return this.installing ? InstallInOtherServerAction.INSTALLING_LABEL :
|
||||
this.server ? `${InstallInOtherServerAction.INSTALL_LABEL} on ${this.server.label}`
|
||||
: InstallInOtherServerAction.INSTALL_LABEL;
|
||||
|
||||
}
|
||||
|
||||
update(): void {
|
||||
this.enabled = false;
|
||||
this.class = RemoteInstallAction.Class;
|
||||
this.class = InstallInOtherServerAction.Class;
|
||||
if (this.installing) {
|
||||
this.enabled = true;
|
||||
this.class = RemoteInstallAction.InstallingClass;
|
||||
this.class = InstallInOtherServerAction.InstallingClass;
|
||||
this.updateLabel();
|
||||
return;
|
||||
}
|
||||
if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer
|
||||
// Installed User Extension
|
||||
&& this.extension && this.extension.local && this.extension.type === ExtensionType.User && this.extension.state === ExtensionState.Installed
|
||||
// Local Workspace Extension
|
||||
&& this.extension.server === this.extensionManagementServerService.localExtensionManagementServer && (isLanguagePackExtension(this.extension.local.manifest) || !isUIExtension(this.extension.local.manifest, this.productService, this.configurationService))
|
||||
// Extension does not exist in remote
|
||||
&& !this.extensionsWorkbenchService.installed.some(e => areSameExtensions(e.identifier, this.extension.identifier) && e.server === this.extensionManagementServerService.remoteExtensionManagementServer)
|
||||
|
||||
if (
|
||||
this.extension && this.extension.local && this.server && this.extension.state === ExtensionState.Installed
|
||||
// disabled by extension kind or it is a language pack extension
|
||||
&& (this.extension.enablementState === EnablementState.DisabledByExtensionKind || isLanguagePackExtension(this.extension.local.manifest))
|
||||
// Not installed in other server and can install in other server
|
||||
&& !this.extensionsWorkbenchService.installed.some(e => areSameExtensions(e.identifier, this.extension.identifier) && e.server === this.server)
|
||||
&& this.extensionsWorkbenchService.canInstall(this.extension)
|
||||
) {
|
||||
this.enabled = true;
|
||||
@@ -346,13 +344,13 @@ export class RemoteInstallAction extends ExtensionAction {
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
if (this.extensionManagementServerService.remoteExtensionManagementServer && !this.installing) {
|
||||
if (this.server && !this.installing) {
|
||||
this.installing = true;
|
||||
this.update();
|
||||
this.extensionsWorkbenchService.open(this.extension);
|
||||
alert(localize('installExtensionStart', "Installing extension {0} started. An editor is now open with more details on this extension", this.extension.displayName));
|
||||
if (this.extension.gallery) {
|
||||
await this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.installFromGallery(this.extension.gallery);
|
||||
await this.server.extensionManagementService.installFromGallery(this.extension.gallery);
|
||||
this.installing = false;
|
||||
this.update();
|
||||
}
|
||||
@@ -360,77 +358,30 @@ export class RemoteInstallAction extends ExtensionAction {
|
||||
}
|
||||
}
|
||||
|
||||
export class LocalInstallAction extends ExtensionAction {
|
||||
|
||||
private static INSTALL_LABEL = localize('install locally', "Install Locally");
|
||||
private static INSTALLING_LABEL = localize('installing', "Installing");
|
||||
|
||||
private static readonly Class = 'extension-action prominent install';
|
||||
private static readonly InstallingClass = 'extension-action install installing';
|
||||
|
||||
updateWhenCounterExtensionChanges: boolean = true;
|
||||
private installing: boolean = false;
|
||||
export class RemoteInstallAction extends InstallInOtherServerAction {
|
||||
|
||||
constructor(
|
||||
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@ILabelService private readonly labelService: ILabelService,
|
||||
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService
|
||||
) {
|
||||
super(`extensions.localinstall`, LocalInstallAction.INSTALL_LABEL, LocalInstallAction.Class, false);
|
||||
this._register(this.labelService.onDidChangeFormatters(() => this.updateLabel(), this));
|
||||
this.updateLabel();
|
||||
this.update();
|
||||
super(`extensions.remoteinstall`, extensionManagementServerService.remoteExtensionManagementServer, extensionsWorkbenchService);
|
||||
}
|
||||
|
||||
private updateLabel(): void {
|
||||
if (this.installing) {
|
||||
this.label = LocalInstallAction.INSTALLING_LABEL;
|
||||
this.tooltip = this.label;
|
||||
return;
|
||||
}
|
||||
this.label = `${LocalInstallAction.INSTALL_LABEL}`;
|
||||
this.tooltip = this.label;
|
||||
}
|
||||
|
||||
export class LocalInstallAction extends InstallInOtherServerAction {
|
||||
|
||||
constructor(
|
||||
@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
|
||||
@IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService
|
||||
) {
|
||||
super(`extensions.localinstall`, extensionManagementServerService.localExtensionManagementServer, extensionsWorkbenchService);
|
||||
}
|
||||
|
||||
update(): void {
|
||||
this.enabled = false;
|
||||
this.class = LocalInstallAction.Class;
|
||||
if (this.installing) {
|
||||
this.enabled = true;
|
||||
this.class = LocalInstallAction.InstallingClass;
|
||||
this.updateLabel();
|
||||
return;
|
||||
}
|
||||
if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer
|
||||
// Installed User Extension
|
||||
&& this.extension && this.extension.local && this.extension.type === ExtensionType.User && this.extension.state === ExtensionState.Installed
|
||||
// Remote UI or Language pack Extension
|
||||
&& this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer && (isLanguagePackExtension(this.extension.local.manifest) || isUIExtension(this.extension.local.manifest, this.productService, this.configurationService))
|
||||
// Extension does not exist in local
|
||||
&& !this.extensionsWorkbenchService.installed.some(e => areSameExtensions(e.identifier, this.extension.identifier) && e.server === this.extensionManagementServerService.localExtensionManagementServer)
|
||||
&& this.extensionsWorkbenchService.canInstall(this.extension)
|
||||
) {
|
||||
this.enabled = true;
|
||||
this.updateLabel();
|
||||
return;
|
||||
}
|
||||
protected getLabel(): string {
|
||||
return this.installing ? InstallInOtherServerAction.INSTALLING_LABEL : localize('install locally', "Install Locally");
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
if (this.extensionManagementServerService.localExtensionManagementServer && !this.installing) {
|
||||
this.installing = true;
|
||||
this.update();
|
||||
this.extensionsWorkbenchService.open(this.extension);
|
||||
alert(localize('installExtensionStart', "Installing extension {0} started. An editor is now open with more details on this extension", this.extension.displayName));
|
||||
if (this.extension.gallery) {
|
||||
await this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(this.extension.gallery);
|
||||
this.installing = false;
|
||||
this.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class UninstallAction extends ExtensionAction {
|
||||
|
||||
@@ -64,8 +64,8 @@ class Extension implements IExtension {
|
||||
@IProductService private readonly productService: IProductService
|
||||
) { }
|
||||
|
||||
get type(): ExtensionType | undefined {
|
||||
return this.local ? this.local.type : undefined;
|
||||
get type(): ExtensionType {
|
||||
return this.local ? this.local.type : ExtensionType.User;
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
|
||||
@@ -34,7 +34,7 @@ export const enum ExtensionState {
|
||||
}
|
||||
|
||||
export interface IExtension {
|
||||
readonly type?: ExtensionType;
|
||||
readonly type: ExtensionType;
|
||||
readonly state: ExtensionState;
|
||||
readonly name: string;
|
||||
readonly displayName: string;
|
||||
|
||||
@@ -21,7 +21,7 @@ import { TestExtensionEnablementService } from 'vs/workbench/services/extensionM
|
||||
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService';
|
||||
import { IURLService } from 'vs/platform/url/common/url';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IPager } from 'vs/base/common/paging';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
@@ -42,6 +42,9 @@ import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { ExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService';
|
||||
import { IProductService } from 'vs/platform/product/common/product';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
|
||||
suite('ExtensionsActions Test', () => {
|
||||
|
||||
@@ -53,7 +56,7 @@ suite('ExtensionsActions Test', () => {
|
||||
didUninstallEvent: Emitter<DidUninstallExtensionEvent>;
|
||||
|
||||
|
||||
suiteSetup(() => {
|
||||
setup(async () => {
|
||||
installEvent = new Emitter<InstallExtensionEvent>();
|
||||
didInstallEvent = new Emitter<DidInstallExtensionEvent>();
|
||||
uninstallEvent = new Emitter<IExtensionIdentifier>();
|
||||
@@ -91,9 +94,7 @@ suite('ExtensionsActions Test', () => {
|
||||
|
||||
instantiationService.set(IExtensionTipsService, instantiationService.createInstance(ExtensionTipsService));
|
||||
instantiationService.stub(IURLService, URLService);
|
||||
});
|
||||
|
||||
setup(async () => {
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', []);
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getExtensionsReport', []);
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage());
|
||||
@@ -1357,6 +1358,682 @@ suite('ExtensionsActions Test', () => {
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test remote install action is enabled for local workspace extension', async () => {
|
||||
// multi server setup
|
||||
const localWorkspaceExtension = aLocalExtension('a', { extensionKind: 'workspace' }, { location: URI.file(`pub.a`) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localWorkspaceExtension]));
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: localWorkspaceExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.RemoteInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.localExtensionManagementServer!);
|
||||
await workbenchService.queryGallery(CancellationToken.None);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.enabled);
|
||||
assert.equal('Install on remote', testObject.label);
|
||||
assert.equal('extension-action prominent install', testObject.class);
|
||||
});
|
||||
|
||||
test('Test remote install action when installing local workspace extension', async (done) => {
|
||||
// multi server setup
|
||||
const remoteExtensionManagementService: IExtensionManagementService = createExtensionManagementService();
|
||||
const localWorkspaceExtension = aLocalExtension('a', { extensionKind: 'workspace' }, { location: URI.file(`pub.a`) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localWorkspaceExtension]), remoteExtensionManagementService);
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.stub(IExtensionsWorkbenchService, workbenchService, 'open', undefined);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: localWorkspaceExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.RemoteInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.localExtensionManagementServer!);
|
||||
await workbenchService.queryGallery(CancellationToken.None);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.enabled);
|
||||
assert.equal('Install on remote', testObject.label);
|
||||
assert.equal('extension-action prominent install', testObject.class);
|
||||
|
||||
remoteExtensionManagementService.installFromGallery = () => new Promise<ILocalExtension>(c => c(aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`) })));
|
||||
const disposable = testObject.onDidChange(() => {
|
||||
if (testObject.label === 'Installing' && testObject.enabled) {
|
||||
disposable.dispose();
|
||||
done();
|
||||
}
|
||||
});
|
||||
testObject.run();
|
||||
});
|
||||
|
||||
test('Test remote install action when installing local workspace extension is finished', async (done) => {
|
||||
// multi server setup
|
||||
const remoteExtensionManagementService: IExtensionManagementService = createExtensionManagementService();
|
||||
const onDidInstallEvent = new Emitter<DidInstallExtensionEvent>();
|
||||
remoteExtensionManagementService.onDidInstallExtension = onDidInstallEvent.event;
|
||||
const localWorkspaceExtension = aLocalExtension('a', { extensionKind: 'workspace' }, { location: URI.file(`pub.a`) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localWorkspaceExtension]), remoteExtensionManagementService);
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.stub(IExtensionsWorkbenchService, workbenchService, 'open', undefined);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: localWorkspaceExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.RemoteInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.localExtensionManagementServer!);
|
||||
await workbenchService.queryGallery(CancellationToken.None);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.enabled);
|
||||
assert.equal('Install on remote', testObject.label);
|
||||
assert.equal('extension-action prominent install', testObject.class);
|
||||
|
||||
const installedExtension = aLocalExtension('a', { extensionKind: 'workspace' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
remoteExtensionManagementService.installFromGallery = () => new Promise<ILocalExtension>(c => c(installedExtension));
|
||||
await testObject.run();
|
||||
assert.ok(testObject.enabled);
|
||||
assert.equal('Install on remote', testObject.label);
|
||||
|
||||
const disposable = testObject.onDidChange(() => {
|
||||
if (testObject.label === 'Install on remote' && !testObject.enabled) {
|
||||
disposable.dispose();
|
||||
done();
|
||||
}
|
||||
});
|
||||
onDidInstallEvent.fire({ identifier: installedExtension.identifier, local: installedExtension, operation: InstallOperation.Install });
|
||||
});
|
||||
|
||||
test('Test remote install action is enabled for disabled local workspace extension', async () => {
|
||||
// multi server setup
|
||||
const localWorkspaceExtension = aLocalExtension('a', { extensionKind: 'workspace' }, { location: URI.file(`pub.a`) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localWorkspaceExtension]));
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
await instantiationService.get(IExtensionEnablementService).setEnablement([localWorkspaceExtension], EnablementState.DisabledGlobally);
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: localWorkspaceExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.RemoteInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.localExtensionManagementServer!);
|
||||
await workbenchService.queryGallery(CancellationToken.None);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.enabled);
|
||||
assert.equal('Install on remote', testObject.label);
|
||||
assert.equal('extension-action prominent install', testObject.class);
|
||||
});
|
||||
|
||||
test('Test remote install action is disabled when extension is not set', async () => {
|
||||
// multi server setup
|
||||
const localWorkspaceExtension = aLocalExtension('a', { extensionKind: 'workspace' }, { location: URI.file(`pub.a`) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localWorkspaceExtension]));
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: localWorkspaceExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.RemoteInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
await workbenchService.queryLocal(extensionManagementServerService.localExtensionManagementServer!);
|
||||
await workbenchService.queryGallery(CancellationToken.None);
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test remote install action is disabled for extension which is not installed', async () => {
|
||||
// multi server setup
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService);
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a')));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.RemoteInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const pager = await workbenchService.queryGallery(CancellationToken.None);
|
||||
testObject.extension = pager.firstPage[0];
|
||||
assert.ok(testObject.extension);
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test remote install action is disabled for local workspace extension which is disabled in env', async () => {
|
||||
// multi server setup
|
||||
const localWorkspaceExtension = aLocalExtension('a', { extensionKind: 'workspace' }, { location: URI.file(`pub.a`) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localWorkspaceExtension]));
|
||||
instantiationService.stub(IWorkbenchEnvironmentService, { disableExtensions: true } as IWorkbenchEnvironmentService);
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: localWorkspaceExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.RemoteInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.localExtensionManagementServer!);
|
||||
await workbenchService.queryGallery(CancellationToken.None);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.extension);
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test remote install action is disabled when remote server is not available', async () => {
|
||||
// single server setup
|
||||
const workbenchService = instantiationService.get(IExtensionsWorkbenchService);
|
||||
const extensionManagementServerService = instantiationService.get(IExtensionManagementServerService);
|
||||
const localWorkspaceExtension = aLocalExtension('a', { extensionKind: 'workspace' }, { location: URI.file(`pub.a`) });
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [localWorkspaceExtension]);
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: localWorkspaceExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.RemoteInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.localExtensionManagementServer!);
|
||||
await workbenchService.queryGallery(CancellationToken.None);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.extension);
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test remote install action is disabled for local workspace extension if it is uninstalled locally', async () => {
|
||||
// multi server setup
|
||||
const extensionManagementService = instantiationService.get(IExtensionManagementService);
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, extensionManagementService);
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const localWorkspaceExtension = aLocalExtension('a', { extensionKind: 'workspace' }, { location: URI.file(`pub.a`) });
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [localWorkspaceExtension]);
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: localWorkspaceExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.RemoteInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.localExtensionManagementServer!);
|
||||
await workbenchService.queryGallery(CancellationToken.None);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.enabled);
|
||||
assert.equal('Install on remote', testObject.label);
|
||||
|
||||
uninstallEvent.fire(localWorkspaceExtension.identifier);
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test remote install action is disabled for local workspace extension if it is installed in remote', async () => {
|
||||
// multi server setup
|
||||
const localWorkspaceExtension = aLocalExtension('a', { extensionKind: 'workspace' }, { location: URI.file(`pub.a`) });
|
||||
const remoteWorkspaceExtension = aLocalExtension('a', { extensionKind: 'workspace' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localWorkspaceExtension]), createExtensionManagementService([remoteWorkspaceExtension]));
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: localWorkspaceExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.RemoteInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.localExtensionManagementServer!);
|
||||
await workbenchService.queryGallery(CancellationToken.None);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.extension);
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test remote install action is disabled for local workspace extension if it cannot be installed', async () => {
|
||||
// multi server setup
|
||||
const localWorkspaceExtension = aLocalExtension('a', { extensionKind: 'workspace' }, { location: URI.file(`pub.a`) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localWorkspaceExtension]));
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: localWorkspaceExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.RemoteInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.localExtensionManagementServer!);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.extension);
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test remote install action is disabled for local ui extension if it is not installed in remote', async () => {
|
||||
// multi server setup
|
||||
const localUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localUIExtension]));
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: localUIExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.RemoteInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.localExtensionManagementServer!);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.extension);
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test remote install action is disabled for local ui extension if it is also installed in remote', async () => {
|
||||
// multi server setup
|
||||
const localUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`) });
|
||||
const remoteUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localUIExtension]), createExtensionManagementService([remoteUIExtension]));
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: localUIExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.RemoteInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.localExtensionManagementServer!);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.extension);
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test remote install action is enabled for locally installed language pack extension', async () => {
|
||||
// multi server setup
|
||||
const languagePackExtension = aLocalExtension('a', { contributes: <IExtensionContributions>{ localizations: [{ languageId: 'de', translations: [] }] } }, { location: URI.file(`pub.a`) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([languagePackExtension]));
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: languagePackExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.RemoteInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.localExtensionManagementServer!);
|
||||
await workbenchService.queryGallery(CancellationToken.None);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.enabled);
|
||||
assert.equal('Install on remote', testObject.label);
|
||||
assert.equal('extension-action prominent install', testObject.class);
|
||||
});
|
||||
|
||||
test('Test remote install action is disabled if local language pack extension is uninstalled', async () => {
|
||||
// multi server setup
|
||||
const extensionManagementService = instantiationService.get(IExtensionManagementService);
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, extensionManagementService);
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const languagePackExtension = aLocalExtension('a', { contributes: <IExtensionContributions>{ localizations: [{ languageId: 'de', translations: [] }] } }, { location: URI.file(`pub.a`) });
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [languagePackExtension]);
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: languagePackExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.RemoteInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.localExtensionManagementServer!);
|
||||
await workbenchService.queryGallery(CancellationToken.None);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.enabled);
|
||||
assert.equal('Install on remote', testObject.label);
|
||||
|
||||
uninstallEvent.fire(languagePackExtension.identifier);
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test local install action is enabled for remote ui extension', async () => {
|
||||
// multi server setup
|
||||
const remoteUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), createExtensionManagementService([remoteUIExtension]));
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: remoteUIExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.LocalInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!);
|
||||
await workbenchService.queryGallery(CancellationToken.None);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.enabled);
|
||||
assert.equal('Install Locally', testObject.label);
|
||||
assert.equal('extension-action prominent install', testObject.class);
|
||||
});
|
||||
|
||||
test('Test local install action when installing remote ui extension', async (done) => {
|
||||
// multi server setup
|
||||
const localExtensionManagementService: IExtensionManagementService = createExtensionManagementService();
|
||||
const remoteUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, localExtensionManagementService, createExtensionManagementService([remoteUIExtension]));
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.stub(IExtensionsWorkbenchService, workbenchService, 'open', undefined);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: remoteUIExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.LocalInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!);
|
||||
await workbenchService.queryGallery(CancellationToken.None);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.enabled);
|
||||
assert.equal('Install Locally', testObject.label);
|
||||
assert.equal('extension-action prominent install', testObject.class);
|
||||
|
||||
localExtensionManagementService.installFromGallery = () => new Promise<ILocalExtension>(c => c(aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`) })));
|
||||
const disposable = testObject.onDidChange(() => {
|
||||
if (testObject.label === 'Installing' && testObject.enabled) {
|
||||
disposable.dispose();
|
||||
done();
|
||||
}
|
||||
});
|
||||
testObject.run();
|
||||
});
|
||||
|
||||
test('Test local install action when installing remote ui extension is finished', async (done) => {
|
||||
// multi server setup
|
||||
const localExtensionManagementService: IExtensionManagementService = createExtensionManagementService();
|
||||
const onDidInstallEvent = new Emitter<DidInstallExtensionEvent>();
|
||||
localExtensionManagementService.onDidInstallExtension = onDidInstallEvent.event;
|
||||
const remoteUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, localExtensionManagementService, createExtensionManagementService([remoteUIExtension]));
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.stub(IExtensionsWorkbenchService, workbenchService, 'open', undefined);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: remoteUIExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.LocalInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!);
|
||||
await workbenchService.queryGallery(CancellationToken.None);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.enabled);
|
||||
assert.equal('Install Locally', testObject.label);
|
||||
assert.equal('extension-action prominent install', testObject.class);
|
||||
|
||||
const installedExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`) });
|
||||
localExtensionManagementService.installFromGallery = () => new Promise<ILocalExtension>(c => c(installedExtension));
|
||||
await testObject.run();
|
||||
assert.ok(testObject.enabled);
|
||||
assert.equal('Install Locally', testObject.label);
|
||||
|
||||
const disposable = testObject.onDidChange(() => {
|
||||
if (testObject.label === 'Install Locally' && !testObject.enabled) {
|
||||
disposable.dispose();
|
||||
done();
|
||||
}
|
||||
});
|
||||
onDidInstallEvent.fire({ identifier: installedExtension.identifier, local: installedExtension, operation: InstallOperation.Install });
|
||||
});
|
||||
|
||||
test('Test local install action is enabled for disabled remote ui extension', async () => {
|
||||
// multi server setup
|
||||
const remoteUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), createExtensionManagementService([remoteUIExtension]));
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
await instantiationService.get(IExtensionEnablementService).setEnablement([remoteUIExtension], EnablementState.DisabledGlobally);
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: remoteUIExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.LocalInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!);
|
||||
await workbenchService.queryGallery(CancellationToken.None);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.enabled);
|
||||
assert.equal('Install Locally', testObject.label);
|
||||
assert.equal('extension-action prominent install', testObject.class);
|
||||
});
|
||||
|
||||
test('Test local install action is disabled when extension is not set', async () => {
|
||||
// multi server setup
|
||||
const remoteUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), createExtensionManagementService([remoteUIExtension]));
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: remoteUIExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.LocalInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!);
|
||||
await workbenchService.queryGallery(CancellationToken.None);
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test local install action is disabled for extension which is not installed', async () => {
|
||||
// multi server setup
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService);
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a')));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.LocalInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const pager = await workbenchService.queryGallery(CancellationToken.None);
|
||||
testObject.extension = pager.firstPage[0];
|
||||
assert.ok(testObject.extension);
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test local install action is disabled for remote ui extension which is disabled in env', async () => {
|
||||
// multi server setup
|
||||
const remoteUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
instantiationService.stub(IWorkbenchEnvironmentService, { disableExtensions: true } as IWorkbenchEnvironmentService);
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), createExtensionManagementService([remoteUIExtension]));
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: remoteUIExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.LocalInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!);
|
||||
await workbenchService.queryGallery(CancellationToken.None);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.extension);
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test local install action is disabled when local server is not available', async () => {
|
||||
// single server setup
|
||||
const remoteUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
const extensionManagementServerService = aSingleRemoteExtensionManagementServerService(instantiationService, createExtensionManagementService([remoteUIExtension]));
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: remoteUIExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.LocalInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!);
|
||||
await workbenchService.queryGallery(CancellationToken.None);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.extension);
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test local install action is disabled for remote ui extension if it is installed in local', async () => {
|
||||
// multi server setup
|
||||
const localUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`) });
|
||||
const remoteUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localUIExtension]), createExtensionManagementService([remoteUIExtension]));
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: localUIExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.LocalInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!);
|
||||
await workbenchService.queryGallery(CancellationToken.None);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.extension);
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test local install action is disabled for remoteUI extension if it is uninstalled locally', async () => {
|
||||
// multi server setup
|
||||
const extensionManagementService = instantiationService.get(IExtensionManagementService);
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), extensionManagementService);
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const remoteUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [remoteUIExtension]);
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: remoteUIExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.LocalInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!);
|
||||
await workbenchService.queryGallery(CancellationToken.None);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.enabled);
|
||||
assert.equal('Install Locally', testObject.label);
|
||||
|
||||
uninstallEvent.fire(remoteUIExtension.identifier);
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test local install action is disabled for remote UI extension if it cannot be installed', async () => {
|
||||
// multi server setup
|
||||
const remoteUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), createExtensionManagementService([remoteUIExtension]));
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: remoteUIExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.LocalInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.extension);
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test local install action is disabled for remote workspace extension if it is not installed in local', async () => {
|
||||
// multi server setup
|
||||
const remoteWorkspaceExtension = aLocalExtension('a', { extensionKind: 'workspace' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), createExtensionManagementService([remoteWorkspaceExtension]));
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: remoteWorkspaceExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.LocalInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.extension);
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test local install action is disabled for remote workspace extension if it is also installed in local', async () => {
|
||||
// multi server setup
|
||||
const localWorkspaceExtension = aLocalExtension('a', { extensionKind: 'workspae' }, { location: URI.file(`pub.a`) });
|
||||
const remoteWorkspaceExtension = aLocalExtension('a', { extensionKind: 'workspace' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService([localWorkspaceExtension]), createExtensionManagementService([remoteWorkspaceExtension]));
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: localWorkspaceExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.LocalInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.extension);
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test('Test local install action is enabled for remotely installed language pack extension', async () => {
|
||||
// multi server setup
|
||||
const languagePackExtension = aLocalExtension('a', { contributes: <IExtensionContributions>{ localizations: [{ languageId: 'de', translations: [] }] } }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), createExtensionManagementService([languagePackExtension]));
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: languagePackExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.LocalInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!);
|
||||
await workbenchService.queryGallery(CancellationToken.None);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.enabled);
|
||||
assert.equal('Install Locally', testObject.label);
|
||||
assert.equal('extension-action prominent install', testObject.class);
|
||||
});
|
||||
|
||||
test('Test local install action is disabled if remote language pack extension is uninstalled', async () => {
|
||||
// multi server setup
|
||||
const extensionManagementService = instantiationService.get(IExtensionManagementService);
|
||||
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), extensionManagementService);
|
||||
instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService);
|
||||
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
|
||||
const languagePackExtension = aLocalExtension('a', { contributes: <IExtensionContributions>{ localizations: [{ languageId: 'de', translations: [] }] } }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [languagePackExtension]);
|
||||
const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService);
|
||||
instantiationService.set(IExtensionsWorkbenchService, workbenchService);
|
||||
|
||||
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', { identifier: languagePackExtension.identifier })));
|
||||
const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.LocalInstallAction);
|
||||
instantiationService.createInstance(ExtensionContainers, [testObject]);
|
||||
|
||||
const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!);
|
||||
await workbenchService.queryGallery(CancellationToken.None);
|
||||
testObject.extension = extensions[0];
|
||||
assert.ok(testObject.enabled);
|
||||
assert.equal('Install Locally', testObject.label);
|
||||
|
||||
uninstallEvent.fire(languagePackExtension.identifier);
|
||||
assert.ok(!testObject.enabled);
|
||||
});
|
||||
|
||||
test(`RecommendToFolderAction`, () => {
|
||||
// TODO: Implement test
|
||||
@@ -1386,4 +2063,61 @@ suite('ExtensionsActions Test', () => {
|
||||
return { firstPage: objects, total: objects.length, pageSize: objects.length, getPage: () => null! };
|
||||
}
|
||||
|
||||
function aSingleRemoteExtensionManagementServerService(instantiationService: TestInstantiationService, remoteExtensionManagementService?: IExtensionManagementService): IExtensionManagementServerService {
|
||||
const remoteExtensionManagementServer: IExtensionManagementServer = {
|
||||
authority: 'vscode-remote',
|
||||
label: 'remote',
|
||||
extensionManagementService: remoteExtensionManagementService || createExtensionManagementService()
|
||||
};
|
||||
return {
|
||||
_serviceBrand: {},
|
||||
localExtensionManagementServer: null,
|
||||
remoteExtensionManagementServer,
|
||||
getExtensionManagementServer: (location: URI) => {
|
||||
if (location.scheme === REMOTE_HOST_SCHEME) {
|
||||
return remoteExtensionManagementServer;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function aMultiExtensionManagementServerService(instantiationService: TestInstantiationService, localExtensionManagementService?: IExtensionManagementService, remoteExtensionManagementService?: IExtensionManagementService): IExtensionManagementServerService {
|
||||
const localExtensionManagementServer: IExtensionManagementServer = {
|
||||
authority: 'vscode-local',
|
||||
label: 'local',
|
||||
extensionManagementService: localExtensionManagementService || createExtensionManagementService()
|
||||
};
|
||||
const remoteExtensionManagementServer: IExtensionManagementServer = {
|
||||
authority: 'vscode-remote',
|
||||
label: 'remote',
|
||||
extensionManagementService: remoteExtensionManagementService || createExtensionManagementService()
|
||||
};
|
||||
return {
|
||||
_serviceBrand: {},
|
||||
localExtensionManagementServer,
|
||||
remoteExtensionManagementServer,
|
||||
getExtensionManagementServer: (location: URI) => {
|
||||
if (location.scheme === Schemas.file) {
|
||||
return localExtensionManagementServer;
|
||||
}
|
||||
if (location.scheme === REMOTE_HOST_SCHEME) {
|
||||
return remoteExtensionManagementServer;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createExtensionManagementService(installed: ILocalExtension[] = []): IExtensionManagementService {
|
||||
return <IExtensionManagementService>{
|
||||
onInstallExtension: Event.None,
|
||||
onDidInstallExtension: Event.None,
|
||||
onUninstallExtension: Event.None,
|
||||
onDidUninstallExtension: Event.None,
|
||||
getInstalled: () => Promise.resolve<ILocalExtension[]>(installed),
|
||||
installFromGallery: (extension: IGalleryExtension) => Promise.reject(new Error('not supported'))
|
||||
};
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -112,7 +112,7 @@ suite('ExtensionsWorkbenchServiceTest', () => {
|
||||
test('test gallery extension', async () => {
|
||||
const expected = aGalleryExtension('expectedName', {
|
||||
displayName: 'expectedDisplayName',
|
||||
version: '1.5',
|
||||
version: '1.5.0',
|
||||
publisherId: 'expectedPublisherId',
|
||||
publisher: 'expectedPublisher',
|
||||
publisherDisplayName: 'expectedPublisherDisplayName',
|
||||
@@ -140,14 +140,14 @@ suite('ExtensionsWorkbenchServiceTest', () => {
|
||||
assert.equal(1, pagedResponse.firstPage.length);
|
||||
const actual = pagedResponse.firstPage[0];
|
||||
|
||||
assert.equal(null, actual.type);
|
||||
assert.equal(ExtensionType.User, actual.type);
|
||||
assert.equal('expectedName', actual.name);
|
||||
assert.equal('expectedDisplayName', actual.displayName);
|
||||
assert.equal('expectedpublisher.expectedname', actual.identifier.id);
|
||||
assert.equal('expectedPublisher', actual.publisher);
|
||||
assert.equal('expectedPublisherDisplayName', actual.publisherDisplayName);
|
||||
assert.equal('1.5', actual.version);
|
||||
assert.equal('1.5', actual.latestVersion);
|
||||
assert.equal('1.5.0', actual.version);
|
||||
assert.equal('1.5.0', actual.latestVersion);
|
||||
assert.equal('expectedDescription', actual.description);
|
||||
assert.equal('uri:icon', actual.iconUrl);
|
||||
assert.equal('fallback:icon', actual.iconUrlFallback);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IExplorerService, IEditableData, IFilesConfiguration, SortOrder, SortOrderConfiguration } from 'vs/workbench/contrib/files/common/files';
|
||||
import { ExplorerItem, ExplorerModel } from 'vs/workbench/contrib/files/common/explorerModel';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
@@ -36,7 +36,7 @@ export class ExplorerService implements IExplorerService {
|
||||
private _onDidChangeEditable = new Emitter<ExplorerItem>();
|
||||
private _onDidSelectResource = new Emitter<{ resource?: URI, reveal?: boolean }>();
|
||||
private _onDidCopyItems = new Emitter<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] | undefined }>();
|
||||
private disposables: IDisposable[] = [];
|
||||
private readonly disposables = new DisposableStore();
|
||||
private editable: { stat: ExplorerItem, data: IEditableData } | undefined;
|
||||
private _sortOrder: SortOrder;
|
||||
private cutItems: ExplorerItem[] | undefined;
|
||||
@@ -88,18 +88,18 @@ export class ExplorerService implements IExplorerService {
|
||||
(root?: URI) => getFileEventsExcludes(this.configurationService, root),
|
||||
(event: IConfigurationChangeEvent) => event.affectsConfiguration(FILES_EXCLUDE_CONFIG)
|
||||
);
|
||||
this.disposables.push(fileEventsFilter);
|
||||
this.disposables.add(fileEventsFilter);
|
||||
|
||||
return fileEventsFilter;
|
||||
}
|
||||
|
||||
@memoize get model(): ExplorerModel {
|
||||
const model = new ExplorerModel(this.contextService);
|
||||
this.disposables.push(model);
|
||||
this.disposables.push(this.fileService.onAfterOperation(e => this.onFileOperation(e)));
|
||||
this.disposables.push(this.fileService.onFileChanges(e => this.onFileChanges(e)));
|
||||
this.disposables.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue<IFilesConfiguration>())));
|
||||
this.disposables.push(this.fileService.onDidChangeFileSystemProviderRegistrations(e => {
|
||||
this.disposables.add(model);
|
||||
this.disposables.add(this.fileService.onAfterOperation(e => this.onFileOperation(e)));
|
||||
this.disposables.add(this.fileService.onFileChanges(e => this.onFileChanges(e)));
|
||||
this.disposables.add(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue<IFilesConfiguration>())));
|
||||
this.disposables.add(this.fileService.onDidChangeFileSystemProviderRegistrations(e => {
|
||||
if (e.added && this.fileSystemProviderSchemes.has(e.scheme)) {
|
||||
// A file system provider got re-registered, we should update all file stats since they might change (got read-only)
|
||||
this.model.roots.forEach(r => r.forgetChildren());
|
||||
@@ -108,7 +108,7 @@ export class ExplorerService implements IExplorerService {
|
||||
this.fileSystemProviderSchemes.add(e.scheme);
|
||||
}
|
||||
}));
|
||||
this.disposables.push(model.onDidChangeRoots(() => this._onDidChangeRoots.fire()));
|
||||
this.disposables.add(model.onDidChangeRoots(() => this._onDidChangeRoots.fire()));
|
||||
|
||||
return model;
|
||||
}
|
||||
@@ -380,6 +380,6 @@ export class ExplorerService implements IExplorerService {
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
dispose(this.disposables);
|
||||
this.disposables.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { TogglePanelAction } from 'vs/workbench/browser/panel';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
@@ -67,11 +66,9 @@ export class ToggleOrSetOutputScrollLockAction extends Action {
|
||||
public static readonly ID = 'workbench.output.action.toggleOutputScrollLock';
|
||||
public static readonly LABEL = nls.localize({ key: 'toggleOutputScrollLock', comment: ['Turn on / off automatic output scrolling'] }, "Toggle Output Scroll Lock");
|
||||
|
||||
private toDispose: IDisposable[] = [];
|
||||
|
||||
constructor(id: string, label: string, @IOutputService private readonly outputService: IOutputService) {
|
||||
super(id, label, 'output-action output-scroll-unlock');
|
||||
this.toDispose.push(this.outputService.onActiveOutputChannel(channel => {
|
||||
this._register(this.outputService.onActiveOutputChannel(channel => {
|
||||
const activeChannel = this.outputService.getActiveChannel();
|
||||
if (activeChannel) {
|
||||
this.setClassAndLabel(activeChannel.scrollLock);
|
||||
@@ -104,11 +101,6 @@ export class ToggleOrSetOutputScrollLockAction extends Action {
|
||||
this.label = nls.localize('outputScrollOff', "Turn Auto Scrolling Off");
|
||||
}
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
super.dispose();
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
}
|
||||
}
|
||||
|
||||
export class SwitchOutputAction extends Action {
|
||||
@@ -188,15 +180,13 @@ export class OpenLogOutputFile extends Action {
|
||||
public static readonly ID = 'workbench.output.action.openLogOutputFile';
|
||||
public static readonly LABEL = nls.localize('openInLogViewer', "Open Log File");
|
||||
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
@IOutputService private readonly outputService: IOutputService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService
|
||||
) {
|
||||
super(OpenLogOutputFile.ID, OpenLogOutputFile.LABEL, 'output-action open-log-file');
|
||||
this.outputService.onActiveOutputChannel(this.update, this, this.disposables);
|
||||
this._register(this.outputService.onActiveOutputChannel(this.update, this));
|
||||
this.update();
|
||||
}
|
||||
|
||||
|
||||
@@ -1151,6 +1151,10 @@ export class SearchView extends ViewletPanel {
|
||||
}
|
||||
|
||||
onQueryChanged(preserveFocus?: boolean): void {
|
||||
if (!this.searchWidget.searchInput.inputBox.isInputValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isRegex = this.searchWidget.searchInput.getRegex();
|
||||
const isWholeWords = this.searchWidget.searchInput.getWholeWords();
|
||||
const isCaseSensitive = this.searchWidget.searchInput.getCaseSensitive();
|
||||
|
||||
@@ -3,25 +3,18 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import * as crypto from 'crypto';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IFileService, IFileStat, IResolveFileResult } from 'vs/platform/files/common/files';
|
||||
import { IFileService, IFileStat } from 'vs/platform/files/common/files';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IWindowConfiguration, IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { endsWith } from 'vs/base/common/strings';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { INotificationService, Severity, IPromptChoice } from 'vs/platform/notification/common/notification';
|
||||
import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { ITextFileService, ITextFileContent } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ITextFileService, } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService';
|
||||
import { IWorkspaceStatsService, Tags } from 'vs/workbench/contrib/stats/electron-browser/workspaceStatsService';
|
||||
|
||||
const SshProtocolMatcher = /^([^@:]+@)?([^:]+):/;
|
||||
const SshUrlMatcher = /^([^@:]+@)?([^:]+):(.+)$/;
|
||||
@@ -43,60 +36,6 @@ const SecondLevelDomainWhitelist = [
|
||||
'rhcloud.com',
|
||||
'google.com'
|
||||
];
|
||||
const ModulesToLookFor = [
|
||||
// Packages that suggest a node server
|
||||
'express',
|
||||
'sails',
|
||||
'koa',
|
||||
'hapi',
|
||||
'socket.io',
|
||||
'restify',
|
||||
// JS frameworks
|
||||
'react',
|
||||
'react-native',
|
||||
'rnpm-plugin-windows',
|
||||
'@angular/core',
|
||||
'@ionic',
|
||||
'vue',
|
||||
'tns-core-modules',
|
||||
// Other interesting packages
|
||||
'aws-sdk',
|
||||
'aws-amplify',
|
||||
'azure',
|
||||
'azure-storage',
|
||||
'firebase',
|
||||
'@google-cloud/common',
|
||||
'heroku-cli'
|
||||
];
|
||||
const PyModulesToLookFor = [
|
||||
'azure',
|
||||
'azure-storage-common',
|
||||
'azure-storage-blob',
|
||||
'azure-storage-file',
|
||||
'azure-storage-queue',
|
||||
'azure-shell',
|
||||
'azure-cosmos',
|
||||
'azure-devtools',
|
||||
'azure-elasticluster',
|
||||
'azure-eventgrid',
|
||||
'azure-functions',
|
||||
'azure-graphrbac',
|
||||
'azure-keyvault',
|
||||
'azure-loganalytics',
|
||||
'azure-monitor',
|
||||
'azure-servicebus',
|
||||
'azure-servicefabric',
|
||||
'azure-storage',
|
||||
'azure-translator',
|
||||
'azure-iothub-device-client',
|
||||
'adal',
|
||||
'pydocumentdb',
|
||||
'botbuilder-core',
|
||||
'botbuilder-schema',
|
||||
'botframework-connector'
|
||||
];
|
||||
|
||||
type Tags = { [index: string]: boolean | number | string | undefined };
|
||||
|
||||
function stripLowLevelDomains(domain: string): string | null {
|
||||
const match = domain.match(SecondLevelDomainMatcher);
|
||||
@@ -212,21 +151,14 @@ export function getHashedRemotesFromUri(workspaceUri: URI, fileService: IFileSer
|
||||
|
||||
export class WorkspaceStats implements IWorkbenchContribution {
|
||||
|
||||
static TAGS: Tags;
|
||||
|
||||
private static DISABLE_WORKSPACE_PROMPT_KEY = 'workspaces.dontPromptToOpen';
|
||||
|
||||
constructor(
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
|
||||
@IWindowService private readonly windowService: IWindowService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IQuickInputService private readonly quickInputService: IQuickInputService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@ITextFileService private readonly textFileService: ITextFileService,
|
||||
@ISharedProcessService private readonly sharedProcessService: ISharedProcessService
|
||||
@ISharedProcessService private readonly sharedProcessService: ISharedProcessService,
|
||||
@IWorkspaceStatsService private readonly workspaceStatsService: IWorkspaceStatsService
|
||||
) {
|
||||
this.report();
|
||||
}
|
||||
@@ -234,7 +166,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
|
||||
private report(): void {
|
||||
|
||||
// Workspace Stats
|
||||
this.resolveWorkspaceTags(this.environmentService.configuration, rootFiles => this.handleWorkspaceFiles(rootFiles))
|
||||
this.workspaceStatsService.getTags()
|
||||
.then(tags => this.reportWorkspaceTags(tags), error => onUnexpectedError(error));
|
||||
|
||||
// Cloud Stats
|
||||
@@ -246,372 +178,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
|
||||
diagnosticsChannel.call('reportWorkspaceStats', this.contextService.getWorkspace());
|
||||
}
|
||||
|
||||
private static searchArray(arr: string[], regEx: RegExp): boolean | undefined {
|
||||
return arr.some(v => v.search(regEx) > -1) || undefined;
|
||||
}
|
||||
|
||||
/* __GDPR__FRAGMENT__
|
||||
"WorkspaceTags" : {
|
||||
"workbench.filesToOpenOrCreate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workbench.filesToDiff" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"workspace.roots" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.empty" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.grunt" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.gulp" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.jake" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.tsconfig" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.jsconfig" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.config.xml" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.vsc.extension" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.asp<NUMBER>" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.sln" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.unity" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.express" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.sails" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.koa" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.hapi" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.socket.io" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.restify" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.rnpm-plugin-windows" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.react" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.@angular/core" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.vue" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.aws-sdk" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.aws-amplify-sdk" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.azure" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.azure-storage" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.@google-cloud/common" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.firebase" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.heroku-cli" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.bower" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.yeoman.code.ext" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.cordova.high" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.cordova.low" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.xamarin.android" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.xamarin.ios" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.android.cpp" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.reactNative" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.ionic" : { "classification" : "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": "true" },
|
||||
"workspace.nativeScript" : { "classification" : "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": "true" },
|
||||
"workspace.java.pom" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.requirements" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.requirements.star" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.Pipfile" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.conda" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.any-azure" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-storage-common" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-storage-blob" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-storage-file" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-storage-queue" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-mgmt" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-shell" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.pulumi-azure" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-cosmos" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-devtools" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-elasticluster" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-eventgrid" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-functions" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-graphrbac" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-keyvault" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-loganalytics" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-monitor" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-servicebus" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-servicefabric" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-storage" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-translator" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-iothub-device-client" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-ml" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-cognitiveservices" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.adal" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.pydocumentdb" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.botbuilder-core" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.botbuilder-schema" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.botframework-connector" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
|
||||
|
||||
}
|
||||
*/
|
||||
private resolveWorkspaceTags(configuration: IWindowConfiguration, participant?: (rootFiles: string[]) => void): Promise<Tags> {
|
||||
const tags: Tags = Object.create(null);
|
||||
|
||||
const state = this.contextService.getWorkbenchState();
|
||||
const workspace = this.contextService.getWorkspace();
|
||||
|
||||
function createHash(uri: URI): string {
|
||||
return crypto.createHash('sha1').update(uri.scheme === Schemas.file ? uri.fsPath : uri.toString()).digest('hex');
|
||||
}
|
||||
|
||||
let workspaceId: string | undefined;
|
||||
switch (state) {
|
||||
case WorkbenchState.EMPTY:
|
||||
workspaceId = undefined;
|
||||
break;
|
||||
case WorkbenchState.FOLDER:
|
||||
workspaceId = createHash(workspace.folders[0].uri);
|
||||
break;
|
||||
case WorkbenchState.WORKSPACE:
|
||||
if (workspace.configuration) {
|
||||
workspaceId = createHash(workspace.configuration);
|
||||
}
|
||||
}
|
||||
|
||||
tags['workspace.id'] = workspaceId;
|
||||
|
||||
const { filesToOpenOrCreate, filesToDiff } = configuration;
|
||||
tags['workbench.filesToOpenOrCreate'] = filesToOpenOrCreate && filesToOpenOrCreate.length || 0;
|
||||
tags['workbench.filesToDiff'] = filesToDiff && filesToDiff.length || 0;
|
||||
|
||||
const isEmpty = state === WorkbenchState.EMPTY;
|
||||
tags['workspace.roots'] = isEmpty ? 0 : workspace.folders.length;
|
||||
tags['workspace.empty'] = isEmpty;
|
||||
|
||||
const folders = !isEmpty ? workspace.folders.map(folder => folder.uri) : this.environmentService.appQuality !== 'stable' && this.findFolders(configuration);
|
||||
if (!folders || !folders.length || !this.fileService) {
|
||||
return Promise.resolve(tags);
|
||||
}
|
||||
|
||||
return this.fileService.resolveAll(folders.map(resource => ({ resource }))).then((files: IResolveFileResult[]) => {
|
||||
const names = (<IFileStat[]>[]).concat(...files.map(result => result.success ? (result.stat!.children || []) : [])).map(c => c.name);
|
||||
const nameSet = names.reduce((s, n) => s.add(n.toLowerCase()), new Set());
|
||||
|
||||
if (participant) {
|
||||
participant(names);
|
||||
}
|
||||
|
||||
tags['workspace.grunt'] = nameSet.has('gruntfile.js');
|
||||
tags['workspace.gulp'] = nameSet.has('gulpfile.js');
|
||||
tags['workspace.jake'] = nameSet.has('jakefile.js');
|
||||
|
||||
tags['workspace.tsconfig'] = nameSet.has('tsconfig.json');
|
||||
tags['workspace.jsconfig'] = nameSet.has('jsconfig.json');
|
||||
tags['workspace.config.xml'] = nameSet.has('config.xml');
|
||||
tags['workspace.vsc.extension'] = nameSet.has('vsc-extension-quickstart.md');
|
||||
|
||||
tags['workspace.ASP5'] = nameSet.has('project.json') && WorkspaceStats.searchArray(names, /^.+\.cs$/i);
|
||||
tags['workspace.sln'] = WorkspaceStats.searchArray(names, /^.+\.sln$|^.+\.csproj$/i);
|
||||
tags['workspace.unity'] = nameSet.has('assets') && nameSet.has('library') && nameSet.has('projectsettings');
|
||||
tags['workspace.npm'] = nameSet.has('package.json') || nameSet.has('node_modules');
|
||||
tags['workspace.bower'] = nameSet.has('bower.json') || nameSet.has('bower_components');
|
||||
|
||||
tags['workspace.java.pom'] = nameSet.has('pom.xml');
|
||||
|
||||
tags['workspace.yeoman.code.ext'] = nameSet.has('vsc-extension-quickstart.md');
|
||||
|
||||
tags['workspace.py.requirements'] = nameSet.has('requirements.txt');
|
||||
tags['workspace.py.requirements.star'] = WorkspaceStats.searchArray(names, /^(.*)requirements(.*)\.txt$/i);
|
||||
tags['workspace.py.Pipfile'] = nameSet.has('pipfile');
|
||||
tags['workspace.py.conda'] = WorkspaceStats.searchArray(names, /^environment(\.yml$|\.yaml$)/i);
|
||||
|
||||
const mainActivity = nameSet.has('mainactivity.cs') || nameSet.has('mainactivity.fs');
|
||||
const appDelegate = nameSet.has('appdelegate.cs') || nameSet.has('appdelegate.fs');
|
||||
const androidManifest = nameSet.has('androidmanifest.xml');
|
||||
|
||||
const platforms = nameSet.has('platforms');
|
||||
const plugins = nameSet.has('plugins');
|
||||
const www = nameSet.has('www');
|
||||
const properties = nameSet.has('properties');
|
||||
const resources = nameSet.has('resources');
|
||||
const jni = nameSet.has('jni');
|
||||
|
||||
if (tags['workspace.config.xml'] &&
|
||||
!tags['workspace.language.cs'] && !tags['workspace.language.vb'] && !tags['workspace.language.aspx']) {
|
||||
if (platforms && plugins && www) {
|
||||
tags['workspace.cordova.high'] = true;
|
||||
} else {
|
||||
tags['workspace.cordova.low'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (tags['workspace.config.xml'] &&
|
||||
!tags['workspace.language.cs'] && !tags['workspace.language.vb'] && !tags['workspace.language.aspx']) {
|
||||
|
||||
if (nameSet.has('ionic.config.json')) {
|
||||
tags['workspace.ionic'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (mainActivity && properties && resources) {
|
||||
tags['workspace.xamarin.android'] = true;
|
||||
}
|
||||
|
||||
if (appDelegate && resources) {
|
||||
tags['workspace.xamarin.ios'] = true;
|
||||
}
|
||||
|
||||
if (androidManifest && jni) {
|
||||
tags['workspace.android.cpp'] = true;
|
||||
}
|
||||
|
||||
function getFilePromises(filename: string, fileService: IFileService, textFileService: ITextFileService, contentHandler: (content: ITextFileContent) => void): Promise<void>[] {
|
||||
return !nameSet.has(filename) ? [] : (folders as URI[]).map(workspaceUri => {
|
||||
const uri = workspaceUri.with({ path: `${workspaceUri.path !== '/' ? workspaceUri.path : ''}/${filename}` });
|
||||
return fileService.exists(uri).then(exists => {
|
||||
if (!exists) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return textFileService.read(uri, { acceptTextOnly: true }).then(contentHandler);
|
||||
}, err => {
|
||||
// Ignore missing file
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function addPythonTags(packageName: string): void {
|
||||
if (PyModulesToLookFor.indexOf(packageName) > -1) {
|
||||
tags['workspace.py.' + packageName] = true;
|
||||
}
|
||||
// cognitive services has a lot of tiny packages. e.g. 'azure-cognitiveservices-search-autosuggest'
|
||||
if (packageName.indexOf('azure-cognitiveservices') > -1) {
|
||||
tags['workspace.py.azure-cognitiveservices'] = true;
|
||||
}
|
||||
if (packageName.indexOf('azure-mgmt') > -1) {
|
||||
tags['workspace.py.azure-mgmt'] = true;
|
||||
}
|
||||
if (packageName.indexOf('azure-ml') > -1) {
|
||||
tags['workspace.py.azure-ml'] = true;
|
||||
}
|
||||
if (!tags['workspace.py.any-azure']) {
|
||||
tags['workspace.py.any-azure'] = /azure/i.test(packageName);
|
||||
}
|
||||
}
|
||||
|
||||
const requirementsTxtPromises = getFilePromises('requirements.txt', this.fileService, this.textFileService, content => {
|
||||
const dependencies: string[] = content.value.split(/\r\n|\r|\n/);
|
||||
for (let dependency of dependencies) {
|
||||
// Dependencies in requirements.txt can have 3 formats: `foo==3.1, foo>=3.1, foo`
|
||||
const format1 = dependency.split('==');
|
||||
const format2 = dependency.split('>=');
|
||||
const packageName = (format1.length === 2 ? format1[0] : format2[0]).trim();
|
||||
addPythonTags(packageName);
|
||||
}
|
||||
});
|
||||
|
||||
const pipfilePromises = getFilePromises('pipfile', this.fileService, this.textFileService, content => {
|
||||
let dependencies: string[] = content.value.split(/\r\n|\r|\n/);
|
||||
|
||||
// We're only interested in the '[packages]' section of the Pipfile
|
||||
dependencies = dependencies.slice(dependencies.indexOf('[packages]') + 1);
|
||||
|
||||
for (let dependency of dependencies) {
|
||||
if (dependency.trim().indexOf('[') > -1) {
|
||||
break;
|
||||
}
|
||||
// All dependencies in Pipfiles follow the format: `<package> = <version, or git repo, or something else>`
|
||||
if (dependency.indexOf('=') === -1) {
|
||||
continue;
|
||||
}
|
||||
const packageName = dependency.split('=')[0].trim();
|
||||
addPythonTags(packageName);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
const packageJsonPromises = getFilePromises('package.json', this.fileService, this.textFileService, content => {
|
||||
try {
|
||||
const packageJsonContents = JSON.parse(content.value);
|
||||
let dependencies = packageJsonContents['dependencies'];
|
||||
let devDependencies = packageJsonContents['devDependencies'];
|
||||
for (let module of ModulesToLookFor) {
|
||||
if ('react-native' === module) {
|
||||
if ((dependencies && dependencies[module]) || (devDependencies && devDependencies[module])) {
|
||||
tags['workspace.reactNative'] = true;
|
||||
}
|
||||
} else if ('tns-core-modules' === module) {
|
||||
if ((dependencies && dependencies[module]) || (devDependencies && devDependencies[module])) {
|
||||
tags['workspace.nativescript'] = true;
|
||||
}
|
||||
} else {
|
||||
if ((dependencies && dependencies[module]) || (devDependencies && devDependencies[module])) {
|
||||
tags['workspace.npm.' + module] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (e) {
|
||||
// Ignore errors when resolving file or parsing file contents
|
||||
}
|
||||
});
|
||||
return Promise.all([...packageJsonPromises, ...requirementsTxtPromises, ...pipfilePromises]).then(() => tags);
|
||||
});
|
||||
}
|
||||
|
||||
private handleWorkspaceFiles(rootFiles: string[]): void {
|
||||
const state = this.contextService.getWorkbenchState();
|
||||
const workspace = this.contextService.getWorkspace();
|
||||
|
||||
// Handle top-level workspace files for local single folder workspace
|
||||
if (state === WorkbenchState.FOLDER) {
|
||||
const workspaceFiles = rootFiles.filter(hasWorkspaceFileExtension);
|
||||
if (workspaceFiles.length > 0) {
|
||||
this.doHandleWorkspaceFiles(workspace.folders[0].uri, workspaceFiles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private doHandleWorkspaceFiles(folder: URI, workspaces: string[]): void {
|
||||
if (this.storageService.getBoolean(WorkspaceStats.DISABLE_WORKSPACE_PROMPT_KEY, StorageScope.WORKSPACE)) {
|
||||
return; // prompt disabled by user
|
||||
}
|
||||
|
||||
const doNotShowAgain: IPromptChoice = {
|
||||
label: localize('never again', "Don't Show Again"),
|
||||
isSecondary: true,
|
||||
run: () => this.storageService.store(WorkspaceStats.DISABLE_WORKSPACE_PROMPT_KEY, true, StorageScope.WORKSPACE)
|
||||
};
|
||||
|
||||
// Prompt to open one workspace
|
||||
if (workspaces.length === 1) {
|
||||
const workspaceFile = workspaces[0];
|
||||
|
||||
this.notificationService.prompt(Severity.Info, localize('workspaceFound', "This folder contains a workspace file '{0}'. Do you want to open it? [Learn more]({1}) about workspace files.", workspaceFile, 'https://go.microsoft.com/fwlink/?linkid=2025315'), [{
|
||||
label: localize('openWorkspace', "Open Workspace"),
|
||||
run: () => this.windowService.openWindow([{ workspaceUri: joinPath(folder, workspaceFile) }])
|
||||
}, doNotShowAgain]);
|
||||
}
|
||||
|
||||
// Prompt to select a workspace from many
|
||||
else if (workspaces.length > 1) {
|
||||
this.notificationService.prompt(Severity.Info, localize('workspacesFound', "This folder contains multiple workspace files. Do you want to open one? [Learn more]({0}) about workspace files.", 'https://go.microsoft.com/fwlink/?linkid=2025315'), [{
|
||||
label: localize('selectWorkspace', "Select Workspace"),
|
||||
run: () => {
|
||||
this.quickInputService.pick(
|
||||
workspaces.map(workspace => ({ label: workspace } as IQuickPickItem)),
|
||||
{ placeHolder: localize('selectToOpen', "Select a workspace to open") }).then(pick => {
|
||||
if (pick) {
|
||||
this.windowService.openWindow([{ workspaceUri: joinPath(folder, pick.label) }]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, doNotShowAgain]);
|
||||
}
|
||||
}
|
||||
|
||||
private findFolders(configuration: IWindowConfiguration): URI[] | undefined {
|
||||
const folder = this.findFolder(configuration);
|
||||
return folder && [folder];
|
||||
}
|
||||
|
||||
private findFolder({ filesToOpenOrCreate, filesToDiff }: IWindowConfiguration): URI | undefined {
|
||||
if (filesToOpenOrCreate && filesToOpenOrCreate.length) {
|
||||
return this.parentURI(filesToOpenOrCreate[0].fileUri);
|
||||
} else if (filesToDiff && filesToDiff.length) {
|
||||
return this.parentURI(filesToDiff[0].fileUri);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private parentURI(uri: URI | undefined): URI | undefined {
|
||||
if (!uri) {
|
||||
return undefined;
|
||||
}
|
||||
const path = uri.path;
|
||||
const i = path.lastIndexOf('/');
|
||||
return i !== -1 ? uri.with({ path: path.substr(0, i) }) : undefined;
|
||||
}
|
||||
|
||||
private reportWorkspaceTags(tags: Tags): void {
|
||||
/* __GDPR__
|
||||
@@ -622,7 +189,6 @@ export class WorkspaceStats implements IWorkbenchContribution {
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('workspce.tags', tags);
|
||||
WorkspaceStats.TAGS = tags;
|
||||
}
|
||||
|
||||
private reportRemoteDomains(workspaceUris: URI[]): void {
|
||||
@@ -689,6 +255,10 @@ export class WorkspaceStats implements IWorkbenchContribution {
|
||||
});
|
||||
}
|
||||
|
||||
private static searchArray(arr: string[], regEx: RegExp): boolean | undefined {
|
||||
return arr.some(v => v.search(regEx) > -1) || undefined;
|
||||
}
|
||||
|
||||
/* __GDPR__FRAGMENT__
|
||||
"AzureTags" : {
|
||||
"java" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
|
||||
|
||||
@@ -0,0 +1,477 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as crypto from 'crypto';
|
||||
import { IFileService, IResolveFileResult, IFileStat } from 'vs/platform/files/common/files';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IWindowService, IWindowConfiguration } from 'vs/platform/windows/common/windows';
|
||||
import { INotificationService, IPromptChoice } from 'vs/platform/notification/common/notification';
|
||||
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { ITextFileService, ITextFileContent } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { localize } from 'vs/nls';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export type Tags = { [index: string]: boolean | number | string | undefined };
|
||||
|
||||
const DISABLE_WORKSPACE_PROMPT_KEY = 'workspaces.dontPromptToOpen';
|
||||
|
||||
const ModulesToLookFor = [
|
||||
// Packages that suggest a node server
|
||||
'express',
|
||||
'sails',
|
||||
'koa',
|
||||
'hapi',
|
||||
'socket.io',
|
||||
'restify',
|
||||
// JS frameworks
|
||||
'react',
|
||||
'react-native',
|
||||
'rnpm-plugin-windows',
|
||||
'@angular/core',
|
||||
'@ionic',
|
||||
'vue',
|
||||
'tns-core-modules',
|
||||
// Other interesting packages
|
||||
'aws-sdk',
|
||||
'aws-amplify',
|
||||
'azure',
|
||||
'azure-storage',
|
||||
'firebase',
|
||||
'@google-cloud/common',
|
||||
'heroku-cli'
|
||||
];
|
||||
const PyModulesToLookFor = [
|
||||
'azure',
|
||||
'azure-storage-common',
|
||||
'azure-storage-blob',
|
||||
'azure-storage-file',
|
||||
'azure-storage-queue',
|
||||
'azure-shell',
|
||||
'azure-cosmos',
|
||||
'azure-devtools',
|
||||
'azure-elasticluster',
|
||||
'azure-eventgrid',
|
||||
'azure-functions',
|
||||
'azure-graphrbac',
|
||||
'azure-keyvault',
|
||||
'azure-loganalytics',
|
||||
'azure-monitor',
|
||||
'azure-servicebus',
|
||||
'azure-servicefabric',
|
||||
'azure-storage',
|
||||
'azure-translator',
|
||||
'azure-iothub-device-client',
|
||||
'adal',
|
||||
'pydocumentdb',
|
||||
'botbuilder-core',
|
||||
'botbuilder-schema',
|
||||
'botframework-connector'
|
||||
];
|
||||
|
||||
export const IWorkspaceStatsService = createDecorator<IWorkspaceStatsService>('workspaceStatsService');
|
||||
|
||||
export interface IWorkspaceStatsService {
|
||||
_serviceBrand: any;
|
||||
getTags(): Promise<Tags>;
|
||||
}
|
||||
|
||||
|
||||
export class WorkspaceStatsService implements IWorkspaceStatsService {
|
||||
_serviceBrand: any;
|
||||
private _tags: Tags;
|
||||
|
||||
constructor(
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
|
||||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
|
||||
@IWindowService private readonly windowService: IWindowService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IQuickInputService private readonly quickInputService: IQuickInputService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@ITextFileService private readonly textFileService: ITextFileService
|
||||
) { }
|
||||
|
||||
public async getTags(): Promise<Tags> {
|
||||
if (!this._tags) {
|
||||
this._tags = await this.resolveWorkspaceTags(this.environmentService.configuration, rootFiles => this.handleWorkspaceFiles(rootFiles));
|
||||
}
|
||||
|
||||
return this._tags;
|
||||
}
|
||||
|
||||
/* __GDPR__FRAGMENT__
|
||||
"WorkspaceTags" : {
|
||||
"workbench.filesToOpenOrCreate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workbench.filesToDiff" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"workspace.roots" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.empty" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.grunt" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.gulp" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.jake" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.tsconfig" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.jsconfig" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.config.xml" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.vsc.extension" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.asp<NUMBER>" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.sln" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.unity" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.express" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.sails" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.koa" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.hapi" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.socket.io" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.restify" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.rnpm-plugin-windows" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.react" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.@angular/core" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.vue" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.aws-sdk" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.aws-amplify-sdk" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.azure" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.azure-storage" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.@google-cloud/common" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.firebase" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.npm.heroku-cli" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.bower" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.yeoman.code.ext" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.cordova.high" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.cordova.low" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.xamarin.android" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.xamarin.ios" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.android.cpp" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.reactNative" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.ionic" : { "classification" : "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": "true" },
|
||||
"workspace.nativeScript" : { "classification" : "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": "true" },
|
||||
"workspace.java.pom" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.requirements" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.requirements.star" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.Pipfile" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.conda" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.any-azure" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-storage-common" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-storage-blob" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-storage-file" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-storage-queue" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-mgmt" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-shell" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.pulumi-azure" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-cosmos" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-devtools" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-elasticluster" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-eventgrid" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-functions" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-graphrbac" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-keyvault" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-loganalytics" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-monitor" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-servicebus" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-servicefabric" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-storage" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-translator" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-iothub-device-client" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-ml" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.azure-cognitiveservices" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.adal" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.pydocumentdb" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.botbuilder-core" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.botbuilder-schema" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"workspace.py.botframework-connector" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
|
||||
|
||||
}
|
||||
*/
|
||||
private resolveWorkspaceTags(configuration: IWindowConfiguration, participant?: (rootFiles: string[]) => void): Promise<Tags> {
|
||||
const tags: Tags = Object.create(null);
|
||||
|
||||
const state = this.contextService.getWorkbenchState();
|
||||
const workspace = this.contextService.getWorkspace();
|
||||
|
||||
function createHash(uri: URI): string {
|
||||
return crypto.createHash('sha1').update(uri.scheme === Schemas.file ? uri.fsPath : uri.toString()).digest('hex');
|
||||
}
|
||||
|
||||
let workspaceId: string | undefined;
|
||||
switch (state) {
|
||||
case WorkbenchState.EMPTY:
|
||||
workspaceId = undefined;
|
||||
break;
|
||||
case WorkbenchState.FOLDER:
|
||||
workspaceId = createHash(workspace.folders[0].uri);
|
||||
break;
|
||||
case WorkbenchState.WORKSPACE:
|
||||
if (workspace.configuration) {
|
||||
workspaceId = createHash(workspace.configuration);
|
||||
}
|
||||
}
|
||||
|
||||
tags['workspace.id'] = workspaceId;
|
||||
|
||||
const { filesToOpenOrCreate, filesToDiff } = configuration;
|
||||
tags['workbench.filesToOpenOrCreate'] = filesToOpenOrCreate && filesToOpenOrCreate.length || 0;
|
||||
tags['workbench.filesToDiff'] = filesToDiff && filesToDiff.length || 0;
|
||||
|
||||
const isEmpty = state === WorkbenchState.EMPTY;
|
||||
tags['workspace.roots'] = isEmpty ? 0 : workspace.folders.length;
|
||||
tags['workspace.empty'] = isEmpty;
|
||||
|
||||
const folders = !isEmpty ? workspace.folders.map(folder => folder.uri) : this.environmentService.appQuality !== 'stable' && this.findFolders(configuration);
|
||||
if (!folders || !folders.length || !this.fileService) {
|
||||
return Promise.resolve(tags);
|
||||
}
|
||||
|
||||
return this.fileService.resolveAll(folders.map(resource => ({ resource }))).then((files: IResolveFileResult[]) => {
|
||||
const names = (<IFileStat[]>[]).concat(...files.map(result => result.success ? (result.stat!.children || []) : [])).map(c => c.name);
|
||||
const nameSet = names.reduce((s, n) => s.add(n.toLowerCase()), new Set());
|
||||
|
||||
if (participant) {
|
||||
participant(names);
|
||||
}
|
||||
|
||||
tags['workspace.grunt'] = nameSet.has('gruntfile.js');
|
||||
tags['workspace.gulp'] = nameSet.has('gulpfile.js');
|
||||
tags['workspace.jake'] = nameSet.has('jakefile.js');
|
||||
|
||||
tags['workspace.tsconfig'] = nameSet.has('tsconfig.json');
|
||||
tags['workspace.jsconfig'] = nameSet.has('jsconfig.json');
|
||||
tags['workspace.config.xml'] = nameSet.has('config.xml');
|
||||
tags['workspace.vsc.extension'] = nameSet.has('vsc-extension-quickstart.md');
|
||||
|
||||
tags['workspace.ASP5'] = nameSet.has('project.json') && this.searchArray(names, /^.+\.cs$/i);
|
||||
tags['workspace.sln'] = this.searchArray(names, /^.+\.sln$|^.+\.csproj$/i);
|
||||
tags['workspace.unity'] = nameSet.has('assets') && nameSet.has('library') && nameSet.has('projectsettings');
|
||||
tags['workspace.npm'] = nameSet.has('package.json') || nameSet.has('node_modules');
|
||||
tags['workspace.bower'] = nameSet.has('bower.json') || nameSet.has('bower_components');
|
||||
|
||||
tags['workspace.java.pom'] = nameSet.has('pom.xml');
|
||||
|
||||
tags['workspace.yeoman.code.ext'] = nameSet.has('vsc-extension-quickstart.md');
|
||||
|
||||
tags['workspace.py.requirements'] = nameSet.has('requirements.txt');
|
||||
tags['workspace.py.requirements.star'] = this.searchArray(names, /^(.*)requirements(.*)\.txt$/i);
|
||||
tags['workspace.py.Pipfile'] = nameSet.has('pipfile');
|
||||
tags['workspace.py.conda'] = this.searchArray(names, /^environment(\.yml$|\.yaml$)/i);
|
||||
|
||||
const mainActivity = nameSet.has('mainactivity.cs') || nameSet.has('mainactivity.fs');
|
||||
const appDelegate = nameSet.has('appdelegate.cs') || nameSet.has('appdelegate.fs');
|
||||
const androidManifest = nameSet.has('androidmanifest.xml');
|
||||
|
||||
const platforms = nameSet.has('platforms');
|
||||
const plugins = nameSet.has('plugins');
|
||||
const www = nameSet.has('www');
|
||||
const properties = nameSet.has('properties');
|
||||
const resources = nameSet.has('resources');
|
||||
const jni = nameSet.has('jni');
|
||||
|
||||
if (tags['workspace.config.xml'] &&
|
||||
!tags['workspace.language.cs'] && !tags['workspace.language.vb'] && !tags['workspace.language.aspx']) {
|
||||
if (platforms && plugins && www) {
|
||||
tags['workspace.cordova.high'] = true;
|
||||
} else {
|
||||
tags['workspace.cordova.low'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (tags['workspace.config.xml'] &&
|
||||
!tags['workspace.language.cs'] && !tags['workspace.language.vb'] && !tags['workspace.language.aspx']) {
|
||||
|
||||
if (nameSet.has('ionic.config.json')) {
|
||||
tags['workspace.ionic'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (mainActivity && properties && resources) {
|
||||
tags['workspace.xamarin.android'] = true;
|
||||
}
|
||||
|
||||
if (appDelegate && resources) {
|
||||
tags['workspace.xamarin.ios'] = true;
|
||||
}
|
||||
|
||||
if (androidManifest && jni) {
|
||||
tags['workspace.android.cpp'] = true;
|
||||
}
|
||||
|
||||
function getFilePromises(filename: string, fileService: IFileService, textFileService: ITextFileService, contentHandler: (content: ITextFileContent) => void): Promise<void>[] {
|
||||
return !nameSet.has(filename) ? [] : (folders as URI[]).map(workspaceUri => {
|
||||
const uri = workspaceUri.with({ path: `${workspaceUri.path !== '/' ? workspaceUri.path : ''}/${filename}` });
|
||||
return fileService.exists(uri).then(exists => {
|
||||
if (!exists) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return textFileService.read(uri, { acceptTextOnly: true }).then(contentHandler);
|
||||
}, err => {
|
||||
// Ignore missing file
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function addPythonTags(packageName: string): void {
|
||||
if (PyModulesToLookFor.indexOf(packageName) > -1) {
|
||||
tags['workspace.py.' + packageName] = true;
|
||||
}
|
||||
// cognitive services has a lot of tiny packages. e.g. 'azure-cognitiveservices-search-autosuggest'
|
||||
if (packageName.indexOf('azure-cognitiveservices') > -1) {
|
||||
tags['workspace.py.azure-cognitiveservices'] = true;
|
||||
}
|
||||
if (packageName.indexOf('azure-mgmt') > -1) {
|
||||
tags['workspace.py.azure-mgmt'] = true;
|
||||
}
|
||||
if (packageName.indexOf('azure-ml') > -1) {
|
||||
tags['workspace.py.azure-ml'] = true;
|
||||
}
|
||||
if (!tags['workspace.py.any-azure']) {
|
||||
tags['workspace.py.any-azure'] = /azure/i.test(packageName);
|
||||
}
|
||||
}
|
||||
|
||||
const requirementsTxtPromises = getFilePromises('requirements.txt', this.fileService, this.textFileService, content => {
|
||||
const dependencies: string[] = content.value.split(/\r\n|\r|\n/);
|
||||
for (let dependency of dependencies) {
|
||||
// Dependencies in requirements.txt can have 3 formats: `foo==3.1, foo>=3.1, foo`
|
||||
const format1 = dependency.split('==');
|
||||
const format2 = dependency.split('>=');
|
||||
const packageName = (format1.length === 2 ? format1[0] : format2[0]).trim();
|
||||
addPythonTags(packageName);
|
||||
}
|
||||
});
|
||||
|
||||
const pipfilePromises = getFilePromises('pipfile', this.fileService, this.textFileService, content => {
|
||||
let dependencies: string[] = content.value.split(/\r\n|\r|\n/);
|
||||
|
||||
// We're only interested in the '[packages]' section of the Pipfile
|
||||
dependencies = dependencies.slice(dependencies.indexOf('[packages]') + 1);
|
||||
|
||||
for (let dependency of dependencies) {
|
||||
if (dependency.trim().indexOf('[') > -1) {
|
||||
break;
|
||||
}
|
||||
// All dependencies in Pipfiles follow the format: `<package> = <version, or git repo, or something else>`
|
||||
if (dependency.indexOf('=') === -1) {
|
||||
continue;
|
||||
}
|
||||
const packageName = dependency.split('=')[0].trim();
|
||||
addPythonTags(packageName);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
const packageJsonPromises = getFilePromises('package.json', this.fileService, this.textFileService, content => {
|
||||
try {
|
||||
const packageJsonContents = JSON.parse(content.value);
|
||||
let dependencies = packageJsonContents['dependencies'];
|
||||
let devDependencies = packageJsonContents['devDependencies'];
|
||||
for (let module of ModulesToLookFor) {
|
||||
if ('react-native' === module) {
|
||||
if ((dependencies && dependencies[module]) || (devDependencies && devDependencies[module])) {
|
||||
tags['workspace.reactNative'] = true;
|
||||
}
|
||||
} else if ('tns-core-modules' === module) {
|
||||
if ((dependencies && dependencies[module]) || (devDependencies && devDependencies[module])) {
|
||||
tags['workspace.nativescript'] = true;
|
||||
}
|
||||
} else {
|
||||
if ((dependencies && dependencies[module]) || (devDependencies && devDependencies[module])) {
|
||||
tags['workspace.npm.' + module] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (e) {
|
||||
// Ignore errors when resolving file or parsing file contents
|
||||
}
|
||||
});
|
||||
return Promise.all([...packageJsonPromises, ...requirementsTxtPromises, ...pipfilePromises]).then(() => tags);
|
||||
});
|
||||
}
|
||||
|
||||
private handleWorkspaceFiles(rootFiles: string[]): void {
|
||||
const state = this.contextService.getWorkbenchState();
|
||||
const workspace = this.contextService.getWorkspace();
|
||||
|
||||
// Handle top-level workspace files for local single folder workspace
|
||||
if (state === WorkbenchState.FOLDER) {
|
||||
const workspaceFiles = rootFiles.filter(hasWorkspaceFileExtension);
|
||||
if (workspaceFiles.length > 0) {
|
||||
this.doHandleWorkspaceFiles(workspace.folders[0].uri, workspaceFiles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private doHandleWorkspaceFiles(folder: URI, workspaces: string[]): void {
|
||||
if (this.storageService.getBoolean(DISABLE_WORKSPACE_PROMPT_KEY, StorageScope.WORKSPACE)) {
|
||||
return; // prompt disabled by user
|
||||
}
|
||||
|
||||
const doNotShowAgain: IPromptChoice = {
|
||||
label: localize('never again', "Don't Show Again"),
|
||||
isSecondary: true,
|
||||
run: () => this.storageService.store(DISABLE_WORKSPACE_PROMPT_KEY, true, StorageScope.WORKSPACE)
|
||||
};
|
||||
|
||||
// Prompt to open one workspace
|
||||
if (workspaces.length === 1) {
|
||||
const workspaceFile = workspaces[0];
|
||||
|
||||
this.notificationService.prompt(Severity.Info, localize('workspaceFound', "This folder contains a workspace file '{0}'. Do you want to open it? [Learn more]({1}) about workspace files.", workspaceFile, 'https://go.microsoft.com/fwlink/?linkid=2025315'), [{
|
||||
label: localize('openWorkspace', "Open Workspace"),
|
||||
run: () => this.windowService.openWindow([{ workspaceUri: joinPath(folder, workspaceFile) }])
|
||||
}, doNotShowAgain]);
|
||||
}
|
||||
|
||||
// Prompt to select a workspace from many
|
||||
else if (workspaces.length > 1) {
|
||||
this.notificationService.prompt(Severity.Info, localize('workspacesFound', "This folder contains multiple workspace files. Do you want to open one? [Learn more]({0}) about workspace files.", 'https://go.microsoft.com/fwlink/?linkid=2025315'), [{
|
||||
label: localize('selectWorkspace', "Select Workspace"),
|
||||
run: () => {
|
||||
this.quickInputService.pick(
|
||||
workspaces.map(workspace => ({ label: workspace } as IQuickPickItem)),
|
||||
{ placeHolder: localize('selectToOpen', "Select a workspace to open") }).then(pick => {
|
||||
if (pick) {
|
||||
this.windowService.openWindow([{ workspaceUri: joinPath(folder, pick.label) }]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, doNotShowAgain]);
|
||||
}
|
||||
}
|
||||
|
||||
private findFolders(configuration: IWindowConfiguration): URI[] | undefined {
|
||||
const folder = this.findFolder(configuration);
|
||||
return folder && [folder];
|
||||
}
|
||||
|
||||
private findFolder({ filesToOpenOrCreate, filesToDiff }: IWindowConfiguration): URI | undefined {
|
||||
if (filesToOpenOrCreate && filesToOpenOrCreate.length) {
|
||||
return this.parentURI(filesToOpenOrCreate[0].fileUri);
|
||||
} else if (filesToDiff && filesToDiff.length) {
|
||||
return this.parentURI(filesToDiff[0].fileUri);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private parentURI(uri: URI | undefined): URI | undefined {
|
||||
if (!uri) {
|
||||
return undefined;
|
||||
}
|
||||
const path = uri.path;
|
||||
const i = path.lastIndexOf('/');
|
||||
return i !== -1 ? uri.with({ path: path.substr(0, i) }) : undefined;
|
||||
}
|
||||
|
||||
private searchArray(arr: string[], regEx: RegExp): boolean | undefined {
|
||||
return arr.some(v => v.search(regEx) > -1) || undefined;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import { IEditorModel } from 'vs/platform/editor/common/editor';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { EditorInput, EditorModel, GroupIdentifier, IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/common/webview';
|
||||
import { UnownedDisposable as Unowned } from 'vs/base/common/lifecycle';
|
||||
|
||||
class WebviewIconsManager {
|
||||
private readonly _icons = new Map<string, { light: URI, dark: URI }>();
|
||||
@@ -58,6 +59,7 @@ export class WebviewEditorInput extends EditorInput {
|
||||
private _name: string;
|
||||
private _iconPath?: { light: URI, dark: URI };
|
||||
private _group?: GroupIdentifier;
|
||||
private readonly _webview: WebviewEditorOverlay;
|
||||
|
||||
constructor(
|
||||
public readonly id: string,
|
||||
@@ -67,14 +69,14 @@ export class WebviewEditorInput extends EditorInput {
|
||||
readonly location: URI;
|
||||
readonly id: ExtensionIdentifier;
|
||||
},
|
||||
public readonly webview: WebviewEditorOverlay,
|
||||
webview: Unowned<WebviewEditorOverlay>,
|
||||
) {
|
||||
super();
|
||||
|
||||
this._name = name;
|
||||
this.extension = extension;
|
||||
|
||||
this._register(webview); // The input owns this webview
|
||||
this._webview = this._register(webview.acquire()); // The input owns this webview
|
||||
}
|
||||
|
||||
public getTypeId(): string {
|
||||
@@ -105,6 +107,10 @@ export class WebviewEditorInput extends EditorInput {
|
||||
this._onDidChangeLabel.fire();
|
||||
}
|
||||
|
||||
public get webview() {
|
||||
return this._webview;
|
||||
}
|
||||
|
||||
public get iconPath() {
|
||||
return this._iconPath;
|
||||
}
|
||||
@@ -147,7 +153,7 @@ export class RevivedWebviewEditorInput extends WebviewEditorInput {
|
||||
readonly id: ExtensionIdentifier
|
||||
},
|
||||
private readonly reviver: (input: WebviewEditorInput) => Promise<void>,
|
||||
webview: WebviewEditorOverlay,
|
||||
webview: Unowned<WebviewEditorOverlay>,
|
||||
) {
|
||||
super(id, viewType, name, extension, webview);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { equals } from 'vs/base/common/arrays';
|
||||
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, toDisposable, UnownedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { values } from 'vs/base/common/map';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
@@ -144,7 +144,7 @@ export class WebviewEditorService implements IWebviewEditorService {
|
||||
): WebviewEditorInput {
|
||||
const webview = this.createWebiew(id, extension, options);
|
||||
|
||||
const webviewInput = this._instantiationService.createInstance(WebviewEditorInput, id, viewType, title, extension, webview);
|
||||
const webviewInput = this._instantiationService.createInstance(WebviewEditorInput, id, viewType, title, extension, new UnownedDisposable(webview));
|
||||
this._editorService.openEditor(webviewInput, { pinned: true, preserveFocus: showOptions.preserveFocus }, showOptions.group);
|
||||
return webviewInput;
|
||||
}
|
||||
@@ -191,7 +191,7 @@ export class WebviewEditorService implements IWebviewEditorService {
|
||||
const promise = new Promise<void>(r => { resolve = r; });
|
||||
this._revivalPool.add(webview, resolve!);
|
||||
return promise;
|
||||
}, webview);
|
||||
}, new UnownedDisposable(webview));
|
||||
|
||||
webviewInput.iconPath = iconPath;
|
||||
|
||||
|
||||
@@ -237,6 +237,13 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe
|
||||
}
|
||||
return getFilePath();
|
||||
|
||||
case 'relativeFileDirname':
|
||||
let dirname = paths.dirname(getFilePath());
|
||||
if (folderUri) {
|
||||
return paths.normalize(paths.relative(getFolderUri().fsPath, dirname));
|
||||
}
|
||||
return dirname;
|
||||
|
||||
case 'fileDirname':
|
||||
return paths.dirname(getFilePath());
|
||||
|
||||
|
||||
@@ -221,7 +221,7 @@ export class FileDialogService implements IFileDialogService {
|
||||
const schema = this.getFileSystemSchema(options);
|
||||
if (this.shouldUseSimplified(schema)) {
|
||||
if (!options.availableFileSystems) {
|
||||
options.availableFileSystems = [schema]; // by default only allow saving in the own file system
|
||||
options.availableFileSystems = this.ensureFileSchema(schema); // always allow file as well
|
||||
}
|
||||
|
||||
return this.saveRemoteResource(options);
|
||||
@@ -239,7 +239,7 @@ export class FileDialogService implements IFileDialogService {
|
||||
const schema = this.getFileSystemSchema(options);
|
||||
if (this.shouldUseSimplified(schema)) {
|
||||
if (!options.availableFileSystems) {
|
||||
options.availableFileSystems = [schema]; // by default only allow loading in the own file system
|
||||
options.availableFileSystems = this.ensureFileSchema(schema); // always allow file as well
|
||||
}
|
||||
|
||||
const uri = await this.pickRemoteResource(options);
|
||||
|
||||
@@ -14,7 +14,7 @@ import { ExtHostCustomersRegistry } from 'vs/workbench/api/common/extHostCustome
|
||||
import { ExtHostContext, ExtHostExtensionServiceShape, IExtHostContext, MainContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier';
|
||||
import { IRPCProtocolLogger, RPCProtocol, RequestInitiator, ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol';
|
||||
import { ResolvedAuthority, RemoteAuthorityResolverError } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { RemoteAuthorityResolverError, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import * as nls from 'vs/nls';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
@@ -249,15 +249,17 @@ export class ExtensionHostProcessManager extends Disposable {
|
||||
return this._extensionHostProcessWorker && Boolean(this._extensionHostProcessWorker.getInspectPort());
|
||||
}
|
||||
|
||||
public async resolveAuthority(remoteAuthority: string): Promise<ResolvedAuthority> {
|
||||
public async resolveAuthority(remoteAuthority: string): Promise<ResolverResult> {
|
||||
const authorityPlusIndex = remoteAuthority.indexOf('+');
|
||||
if (authorityPlusIndex === -1) {
|
||||
// This authority does not need to be resolved, simply parse the port number
|
||||
const pieces = remoteAuthority.split(':');
|
||||
return Promise.resolve({
|
||||
authority: remoteAuthority,
|
||||
host: pieces[0],
|
||||
port: parseInt(pieces[1], 10)
|
||||
authority: {
|
||||
authority: remoteAuthority,
|
||||
host: pieces[0],
|
||||
port: parseInt(pieces[1], 10)
|
||||
}
|
||||
});
|
||||
}
|
||||
const proxy = await this._getExtensionHostProcessProxy();
|
||||
|
||||
@@ -76,19 +76,20 @@ export class RemoteExtensionHostClient extends Disposable implements IExtensionH
|
||||
webSocketFactory: this._webSocketFactory,
|
||||
addressProvider: {
|
||||
getAddress: async () => {
|
||||
const { host, port } = await this.remoteAuthorityResolverService.resolveAuthority(this._initDataProvider.remoteAuthority);
|
||||
return { host, port };
|
||||
const { authority } = await this.remoteAuthorityResolverService.resolveAuthority(this._initDataProvider.remoteAuthority);
|
||||
return { host: authority.host, port: authority.port };
|
||||
}
|
||||
},
|
||||
signService: this._signService
|
||||
};
|
||||
return this.remoteAuthorityResolverService.resolveAuthority(this._initDataProvider.remoteAuthority).then((resolvedAuthority) => {
|
||||
return this.remoteAuthorityResolverService.resolveAuthority(this._initDataProvider.remoteAuthority).then((resolverResult) => {
|
||||
|
||||
const startParams: IRemoteExtensionHostStartParams = {
|
||||
language: platform.language,
|
||||
debugId: this._environmentService.debugExtensionHost.debugId,
|
||||
break: this._environmentService.debugExtensionHost.break,
|
||||
port: this._environmentService.debugExtensionHost.port,
|
||||
env: resolverResult.options && resolverResult.options.extensionHostEnv
|
||||
};
|
||||
|
||||
const extDevLocs = this._environmentService.extensionDevelopmentLocationURI;
|
||||
|
||||
@@ -19,7 +19,7 @@ import { IExtensionEnablementService } from 'vs/workbench/services/extensionMana
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IInitDataProvider, RemoteExtensionHostClient } from 'vs/workbench/services/extensions/common/remoteExtensionHostClient';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { IRemoteAuthorityResolverService, ResolvedAuthority, RemoteAuthorityResolverError } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { IRemoteAuthorityResolverService, RemoteAuthorityResolverError, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil';
|
||||
import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -424,8 +424,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
||||
const extensionHost = this._extensionHostProcessManagers[0];
|
||||
this._remoteAuthorityResolverService.clearResolvedAuthority(remoteAuthority);
|
||||
try {
|
||||
const resolvedAuthority = await extensionHost.resolveAuthority(remoteAuthority);
|
||||
this._remoteAuthorityResolverService.setResolvedAuthority(resolvedAuthority);
|
||||
const result = await extensionHost.resolveAuthority(remoteAuthority);
|
||||
this._remoteAuthorityResolverService.setResolvedAuthority(result.authority, result.options);
|
||||
} catch (err) {
|
||||
this._remoteAuthorityResolverService.setResolvedAuthorityError(remoteAuthority, err);
|
||||
}
|
||||
@@ -446,7 +446,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
||||
localExtensions = localExtensions.filter(extension => this._isEnabled(extension));
|
||||
|
||||
if (remoteAuthority) {
|
||||
let resolvedAuthority: ResolvedAuthority;
|
||||
let resolvedAuthority: ResolverResult;
|
||||
|
||||
try {
|
||||
resolvedAuthority = await extensionHost.resolveAuthority(remoteAuthority);
|
||||
@@ -468,7 +468,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
||||
}
|
||||
|
||||
// set the resolved authority
|
||||
this._remoteAuthorityResolverService.setResolvedAuthority(resolvedAuthority);
|
||||
this._remoteAuthorityResolverService.setResolvedAuthority(resolvedAuthority.authority, resolvedAuthority.options);
|
||||
|
||||
// monitor for breakage
|
||||
const connection = this._remoteAgentService.getConnection();
|
||||
|
||||
@@ -12,33 +12,39 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { TestStorageService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
|
||||
|
||||
class TestKeyboardMapperFactory extends BrowserKeyboardMapperFactoryBase {
|
||||
constructor(notificationService: INotificationService, storageService: IStorageService, commandService: ICommandService) {
|
||||
super(notificationService, storageService, commandService);
|
||||
|
||||
let keymapInfos: IKeymapInfo[] = KeyboardLayoutContribution.INSTANCE.layoutInfos;
|
||||
const keymapInfos: IKeymapInfo[] = KeyboardLayoutContribution.INSTANCE.layoutInfos;
|
||||
this._keymapInfos.push(...keymapInfos.map(info => (new KeymapInfo(info.layout, info.secondaryLayouts, info.mapping, info.isUserKeyboardLayout))));
|
||||
this._mru = this._keymapInfos;
|
||||
this._initialized = true;
|
||||
this.onKeyboardLayoutChanged();
|
||||
const usLayout = this.getUSStandardLayout();
|
||||
if (usLayout) {
|
||||
this.setActiveKeyMapping(usLayout.mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
suite('keyboard layout loader', () => {
|
||||
let instantiationService: TestInstantiationService = new TestInstantiationService();
|
||||
let notitifcationService = instantiationService.stub(INotificationService, {});
|
||||
let storageService = instantiationService.stub(IStorageService, {});
|
||||
let notitifcationService = instantiationService.stub(INotificationService, new TestNotificationService());
|
||||
let storageService = instantiationService.stub(IStorageService, new TestStorageService());
|
||||
|
||||
let commandService = instantiationService.stub(ICommandService, {});
|
||||
let instance = new TestKeyboardMapperFactory(notitifcationService, storageService, commandService);
|
||||
|
||||
test.skip('load default US keyboard layout', () => {
|
||||
test('load default US keyboard layout', () => {
|
||||
assert.notEqual(instance.activeKeyboardLayout, null);
|
||||
assert.equal(instance.activeKeyboardLayout!.isUSStandard, true);
|
||||
});
|
||||
|
||||
test.skip('isKeyMappingActive', () => {
|
||||
test('isKeyMappingActive', () => {
|
||||
assert.equal(instance.isKeyMappingActive({
|
||||
KeyA: {
|
||||
value: 'a',
|
||||
|
||||
@@ -121,8 +121,8 @@ export class RemoteAgentConnection extends Disposable implements IRemoteAgentCon
|
||||
} else {
|
||||
this._onReconnecting.fire(undefined);
|
||||
}
|
||||
const { host, port } = await this._remoteAuthorityResolverService.resolveAuthority(this.remoteAuthority);
|
||||
return { host, port };
|
||||
const { authority } = await this._remoteAuthorityResolverService.resolveAuthority(this.remoteAuthority);
|
||||
return { host: authority.host, port: authority.port };
|
||||
}
|
||||
},
|
||||
signService: this._signService
|
||||
|
||||
@@ -105,8 +105,8 @@ export class TunnelService implements ITunnelService {
|
||||
webSocketFactory: nodeWebSocketFactory,
|
||||
addressProvider: {
|
||||
getAddress: async () => {
|
||||
const { host, port } = await this.remoteAuthorityResolverService.resolveAuthority(remoteAuthority);
|
||||
return { host, port };
|
||||
const { authority } = await this.remoteAuthorityResolverService.resolveAuthority(remoteAuthority);
|
||||
return { host: authority.host, port: authority.port };
|
||||
}
|
||||
},
|
||||
signService: this.signService
|
||||
|
||||
@@ -168,6 +168,7 @@ registerSingleton(IMenubarService, MenubarService);
|
||||
registerSingleton(IURLService, RelayURLService);
|
||||
registerSingleton(ITunnelService, TunnelService, true);
|
||||
registerSingleton(ICredentialsService, KeytarCredentialsService, true);
|
||||
registerSingleton(IWorkspaceStatsService, WorkspaceStatsService, true);
|
||||
|
||||
//#endregion
|
||||
|
||||
@@ -456,6 +457,7 @@ import 'vs/workbench/contrib/experiments/electron-browser/experiments.contributi
|
||||
|
||||
// Issues
|
||||
import 'vs/workbench/contrib/issue/electron-browser/issue.contribution';
|
||||
import { IWorkspaceStatsService, WorkspaceStatsService } from 'vs/workbench/contrib/stats/electron-browser/workspaceStatsService';
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
// SQL
|
||||
|
||||
@@ -9902,10 +9902,10 @@ vscode-proxy-agent@0.4.0:
|
||||
https-proxy-agent "2.2.1"
|
||||
socks-proxy-agent "4.0.1"
|
||||
|
||||
vscode-ripgrep@^1.5.4:
|
||||
version "1.5.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.5.4.tgz#dae1c3eef350513299341cdf96e622c00b548eff"
|
||||
integrity sha512-Bs8SvFAkR0QHf09J46VgNo19yRikOtj/f0zHzK3AM3ICjCGNN/BNoG9of6zGVHUTO+6Mk1RbKglyOHLKr8D1lg==
|
||||
vscode-ripgrep@^1.5.5:
|
||||
version "1.5.5"
|
||||
resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.5.5.tgz#24c0e9cb356cf889c98e15ecb58f9cf654a1d961"
|
||||
integrity sha512-OrPrAmcun4+uZAuNcQvE6CCPskh+5AsjANod/Q3zRcJcGNxgoOSGlQN9RPtatkUNmkN8Nn8mZBnS1jMylu/dKg==
|
||||
|
||||
vscode-sqlite3@4.0.8:
|
||||
version "4.0.8"
|
||||
|
||||