Merge from vscode a348d103d1256a06a2c9b3f9b406298a9fef6898 (#15681)

* Merge from vscode a348d103d1256a06a2c9b3f9b406298a9fef6898

* Fixes and cleanup

* Distro

* Fix hygiene yarn

* delete no yarn lock changes file

* Fix hygiene

* Fix layer check

* Fix CI

* Skip lib checks

* Remove tests deleted in vs code

* Fix tests

* Distro

* Fix tests and add removed extension point

* Skip failing notebook tests for now

* Disable broken tests and cleanup build folder

* Update yarn.lock and fix smoke tests

* Bump sqlite

* fix contributed actions and file spacing

* Fix user data path

* Update yarn.locks

Co-authored-by: ADS Merger <karlb@microsoft.com>
This commit is contained in:
Charles Gagnon
2021-06-17 08:17:11 -07:00
committed by GitHub
parent fdcb97c7f7
commit 3cb2f552a6
2582 changed files with 124827 additions and 87099 deletions

View File

@@ -1,5 +1,5 @@
{
"extends": "../shared.tsconfig.json",
"extends": "../tsconfig.base.json",
"compileOnSave": true,
"compilerOptions": {
"outDir": "./out",

View File

@@ -1,5 +1,5 @@
{
"extends": "../shared.tsconfig.json",
"extends": "../tsconfig.base.json",
"compileOnSave": true,
"compilerOptions": {
"outDir": "./out",

View File

@@ -88,7 +88,7 @@ export class TextKeyValue extends KeyValue {
this.container.addItem(this.text, this.valueFlex);
}
public setValue(newValue: string) {
public override setValue(newValue: string) {
super.setValue(newValue);
this.text.value = newValue;
}
@@ -125,7 +125,7 @@ export abstract class BaseInputKeyValue extends KeyValue {
this.container.addItem(inputContainer, this.valueFlex);
}
public setValue(newValue: string) {
public override setValue(newValue: string) {
super.setValue(newValue);
this.input.value = newValue;
}
@@ -161,7 +161,7 @@ export class LinkKeyValue extends KeyValue {
this.container.addItem(this.link, this.valueFlex);
}
public setValue(newValue: string) {
public override setValue(newValue: string) {
super.setValue(newValue);
this.link.label = newValue;
}

View File

@@ -15,7 +15,7 @@ export class ControllerDashboard extends Dashboard {
super(loc.arcControllerDashboard(_controllerModel.info.name), 'ArcDataControllerDashboard');
}
public async showDashboard(): Promise<void> {
public override async showDashboard(): Promise<void> {
await super.showDashboard();
// Kick off the model refresh but don't wait on it since that's all handled with callbacks anyways
this._controllerModel.refresh(false).catch(err => console.log(`Error refreshing Controller dashboard ${err}`));

View File

@@ -32,7 +32,7 @@ export class MiaaComputeAndStoragePage extends DashboardPage {
private readonly _azdataApi: azdataExt.IExtension;
constructor(protected modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _miaaModel: MiaaModel) {
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _miaaModel: MiaaModel) {
super(modelView, dashboard);
this._azdataApi = vscode.extensions.getExtension(azdataExt.extension.name)?.exports;

View File

@@ -18,7 +18,7 @@ export class MiaaDashboard extends Dashboard {
super(loc.miaaDashboard(_miaaModel.info.name), 'ArcMiaaDashboard');
}
public async showDashboard(): Promise<void> {
public override async showDashboard(): Promise<void> {
await super.showDashboard();
// Kick off the model refreshes but don't wait on it since that's all handled with callbacks anyways
this._controllerModel.refresh().catch(err => console.log(`Error refreshing controller model for MIAA dashboard ${err}`));

View File

@@ -47,7 +47,7 @@ export class PostgresComputeAndStoragePage extends DashboardPage {
private readonly _azdataApi: azdataExt.IExtension;
constructor(protected modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _postgresModel: PostgresModel) {
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _postgresModel: PostgresModel) {
super(modelView, dashboard);
this._azdataApi = vscode.extensions.getExtension(azdataExt.extension.name)?.exports;

View File

@@ -14,7 +14,7 @@ export class PostgresConnectionStringsPage extends DashboardPage {
private keyValueContainer?: KeyValueContainer;
private connectionStringsLoading!: azdata.LoadingComponent;
constructor(protected modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _postgresModel: PostgresModel) {
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _postgresModel: PostgresModel) {
super(modelView, dashboard);
this.disposables.push(this._postgresModel.onConfigUpdated(

View File

@@ -24,7 +24,7 @@ export class PostgresDashboard extends Dashboard {
super(loc.postgresDashboard(_postgresModel.info.name), 'ArcPgDashboard');
}
public async showDashboard(): Promise<void> {
public override async showDashboard(): Promise<void> {
await super.showDashboard();
// Kick off the model refresh but don't wait on it since that's all handled with callbacks anyways

View File

@@ -37,7 +37,7 @@ export class PostgresOverviewPage extends DashboardPage {
private readonly _azdataApi: azdataExt.IExtension;
constructor(protected modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _controllerModel: ControllerModel, private _postgresModel: PostgresModel) {
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _controllerModel: ControllerModel, private _postgresModel: PostgresModel) {
super(modelView, dashboard);
this._azdataApi = vscode.extensions.getExtension(azdataExt.extension.name)?.exports;

View File

@@ -39,7 +39,7 @@ export abstract class PostgresParametersPage extends DashboardPage {
protected readonly _azdataApi: azdataExt.IExtension;
constructor(protected modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, protected _postgresModel: PostgresModel) {
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, protected _postgresModel: PostgresModel) {
super(modelView, dashboard);
this._azdataApi = vscode.extensions.getExtension(azdataExt.extension.name)?.exports;

View File

@@ -17,7 +17,7 @@ export class PostgresPropertiesPage extends DashboardPage {
private loading?: azdata.LoadingComponent;
private keyValueContainer?: KeyValueContainer;
constructor(protected modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _controllerModel: ControllerModel, private _postgresModel: PostgresModel) {
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _controllerModel: ControllerModel, private _postgresModel: PostgresModel) {
super(modelView, dashboard);
this.disposables.push(this._postgresModel.onConfigUpdated(

View File

@@ -36,7 +36,7 @@ export class PostgresResourceHealthPage extends DashboardPage {
private coordinatorData: PodHealthModel[] = [];
private podsData: PodHealthModel[] = [];
constructor(protected modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _postgresModel: PostgresModel) {
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _postgresModel: PostgresModel) {
super(modelView, dashboard);
this.disposables.push(

View File

@@ -13,7 +13,7 @@ import { ResourceType } from 'arc';
import { PostgresModel } from '../../../models/postgresModel';
export class PostgresSupportRequestPage extends DashboardPage {
constructor(protected modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _controllerModel: ControllerModel, private _postgresModel: PostgresModel) {
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _controllerModel: ControllerModel, private _postgresModel: PostgresModel) {
super(modelView, dashboard);
}

View File

@@ -212,7 +212,7 @@ export class ConnectToControllerDialog extends ControllerDialogBase {
return this.namespaceInputBox;
}
protected getComponents() {
protected override getComponents() {
return [
...super.getComponents(),
{
@@ -221,7 +221,7 @@ export class ConnectToControllerDialog extends ControllerDialogBase {
}];
}
protected initializeFields(controllerInfo: ControllerInfo | undefined, password: string | undefined) {
protected override initializeFields(controllerInfo: ControllerInfo | undefined, password: string | undefined) {
super.initializeFields(controllerInfo, password);
this.rememberPwCheckBox = this.modelBuilder.checkBox()
.withProperties<azdata.CheckBoxProperties>({
@@ -282,7 +282,7 @@ export class PasswordToControllerDialog extends ControllerDialogBase {
return this.passwordInputBox;
}
protected readonlyFields(): azdata.Component[] {
protected override readonlyFields(): azdata.Component[] {
return [
this.urlInputBox,
...this.kubeConfigInputBox.items,
@@ -331,7 +331,7 @@ export class PasswordToControllerDialog extends ControllerDialogBase {
return true;
}
public showDialog(controllerInfo?: ControllerInfo): azdata.window.Dialog {
public override showDialog(controllerInfo?: ControllerInfo): azdata.window.Dialog {
const dialog = super.showDialog(controllerInfo);
dialog.okButton.label = loc.ok;
return dialog;

View File

@@ -37,7 +37,7 @@ export class ControllerTreeNode extends TreeNode {
});
}
public async getChildren(): Promise<TreeNode[]> {
public override async getChildren(): Promise<TreeNode[]> {
try {
await this.model.refresh(false);
this.updateChildren(this.model.registrations);
@@ -60,7 +60,7 @@ export class ControllerTreeNode extends TreeNode {
return this._children.length > 0 ? this._children : [new NoInstancesTreeNode()];
}
public async openDashboard(): Promise<void> {
public override async openDashboard(): Promise<void> {
const controllerDashboard = new ControllerDashboard(this.model);
await controllerDashboard.showDashboard();
}

View File

@@ -19,7 +19,7 @@ export class MiaaTreeNode extends ResourceTreeNode<MiaaModel> {
super(model.info.name, vscode.TreeItemCollapsibleState.None, ResourceType.sqlManagedInstances, model);
}
public async openDashboard(): Promise<void> {
public override async openDashboard(): Promise<void> {
const miaaDashboard = new MiaaDashboard(this._controllerModel, this.model);
await miaaDashboard.showDashboard();
}

View File

@@ -19,7 +19,7 @@ export class PostgresTreeNode extends ResourceTreeNode<PostgresModel> {
super(model.info.name, vscode.TreeItemCollapsibleState.None, ResourceType.postgresInstances, model);
}
public async openDashboard(): Promise<void> {
public override async openDashboard(): Promise<void> {
const postgresDashboard = new PostgresDashboard(this._context, this._controllerModel, this.model);
await postgresDashboard.showDashboard();
}

View File

@@ -17,7 +17,7 @@ export class RefreshTreeNode extends TreeNode {
super(loc.refreshToEnterCredentials, vscode.TreeItemCollapsibleState.None, '');
}
public command: vscode.Command = {
public override command: vscode.Command = {
command: refreshActionId,
title: loc.refreshToEnterCredentials,
arguments: [this._parent]

View File

@@ -20,6 +20,6 @@ export abstract class TreeNode extends vscode.TreeItem {
public async openDashboard(): Promise<void> { }
iconPath = getResourceTypeIcon(this.resourceType);
contextValue = this.resourceType;
override iconPath = getResourceTypeIcon(this.resourceType);
override contextValue = this.resourceType;
}

View File

@@ -1,5 +1,5 @@
{
"extends": "../shared.tsconfig.json",
"extends": "../tsconfig.base.json",
"compileOnSave": true,
"compilerOptions": {
"experimentalDecorators": true,

View File

@@ -1,5 +1,5 @@
{
"extends": "../shared.tsconfig.json",
"extends": "../tsconfig.base.json",
"compileOnSave": true,
"compilerOptions": {
"outDir": "./out",

View File

@@ -142,7 +142,7 @@ export class AzureDeviceCode extends AzureAuth {
}
public async autoOAuthCancelled(): Promise<void> {
public override async autoOAuthCancelled(): Promise<void> {
return azdata.accounts.endAutoOAuthDeviceCode();
}
}

View File

@@ -1,5 +1,5 @@
{
"extends": "../shared.tsconfig.json",
"extends": "../tsconfig.base.json",
"compilerOptions": {
"outDir": "./out",
"strict": false,

View File

@@ -1,5 +1,5 @@
{
"extends": "../shared.tsconfig.json",
"extends": "../tsconfig.base.json",
"compileOnSave": true,
"compilerOptions": {
"module": "commonjs",

View File

@@ -9,7 +9,7 @@
"vscode": "^1.52.0"
},
"scripts": {
"update-grammar": "node ../node_modules/.bin/vscode-grammar-updater mmims/language-batchfile grammars/batchfile.cson ./syntaxes/batchfile.tmLanguage.json"
"update-grammar": "node ../node_modules/vscode-grammar-updater/bin mmims/language-batchfile grammars/batchfile.cson ./syntaxes/batchfile.tmLanguage.json"
},
"contributes": {
"languages": [

View File

@@ -32,7 +32,7 @@ export class KerberosAuth extends SslAuth implements Authentication {
super();
}
applyToRequest(requestOptions: request.Options): void {
override applyToRequest(requestOptions: request.Options): void {
super.applyToRequest(requestOptions);
if (requestOptions && requestOptions.headers) {
requestOptions.headers['Authorization'] = `Negotiate ${this.kerberosToken}`;
@@ -45,7 +45,7 @@ export class BasicAuth extends SslAuth implements Authentication {
super();
}
applyToRequest(requestOptions: request.Options): void {
override applyToRequest(requestOptions: request.Options): void {
super.applyToRequest(requestOptions);
requestOptions.auth = {
username: this.username, password: this.password
@@ -56,7 +56,7 @@ export class BasicAuth extends SslAuth implements Authentication {
export class OAuthWithSsl extends SslAuth implements Authentication {
public accessToken: string = '';
applyToRequest(requestOptions: request.Options): void {
override applyToRequest(requestOptions: request.Options): void {
super.applyToRequest(requestOptions);
if (requestOptions && requestOptions.headers) {
requestOptions.headers['Authorization'] = `Bearer ${this.accessToken}`;

View File

@@ -28,7 +28,7 @@ abstract class ControllerTreeNode extends TreeNode {
return this.children as ControllerTreeNode[];
}
public refresh(): void {
public override refresh(): void {
super.refresh();
this.treeChangeHandler.notifyNodeChanged(this);
}
@@ -98,7 +98,7 @@ export class ControllerRootNode extends ControllerTreeNode {
super('root', undefined, treeChangeHandler, undefined, BdcItemType.controllerRoot);
}
public async getChildren(): Promise<ControllerNode[]> {
public override async getChildren(): Promise<ControllerNode[]> {
return this.children as ControllerNode[];
}
@@ -168,7 +168,7 @@ export class ControllerNode extends ControllerTreeNode {
this.description = description;
}
public async getChildren(): Promise<ControllerTreeNode[] | undefined> {
public override async getChildren(): Promise<ControllerTreeNode[] | undefined> {
if (this.children && this.children.length > 0) {
this.clearChildren();
}
@@ -209,7 +209,7 @@ export class ControllerNode extends ControllerTreeNode {
this._password = pw;
}
public set label(label: string) {
public override set label(label: string) {
super.label = label || this.generateLabel();
}
@@ -229,15 +229,15 @@ export class ControllerNode extends ControllerTreeNode {
return label;
}
public get label(): string {
public override get label(): string {
return super.label;
}
public set description(description: string) {
public override set description(description: string) {
super.description = description || super.label;
}
public get description(): string {
public override get description(): string {
return super.description;
}
}

View File

@@ -1,5 +1,5 @@
{
"extends": "../shared.tsconfig.json",
"extends": "../tsconfig.base.json",
"compileOnSave": true,
"compilerOptions": {
"outDir": "./out",

View File

@@ -1,5 +1,5 @@
{
"extends": "../shared.tsconfig.json",
"extends": "../tsconfig.base.json",
"compileOnSave": true,
"compilerOptions": {
"outDir": "./out",

View File

@@ -1,5 +1,5 @@
{
"extends": "../../shared.tsconfig.json",
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"resolveJsonModule": true,
"outDir": "./out"

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -8,6 +8,7 @@
"engines": {
"vscode": "^1.0.0"
},
"icon": "images/icon.png",
"activationEvents": [
"onLanguage:json",
"onLanguage:jsonc"
@@ -22,6 +23,12 @@
"jsonc-parser": "^2.2.1",
"vscode-nls": "^4.1.1"
},
"capabilities": {
"virtualWorkspaces": true,
"untrustedWorkspaces": {
"supported": true
}
},
"contributes": {
"languages": [
{
@@ -40,8 +47,12 @@
"keybindings.json",
"extensions.json",
"argv.json",
"profiles.json"
]
"profiles.json",
".devcontainer.json"
],
"filenamePatterns": [
"**/.devcontainer/devcontainer.json"
]
}
],
"jsonValidation": [

View File

@@ -19,6 +19,93 @@
"type": "integer"
}
},
"portsAttributes": {
"type": "object",
"patternProperties": {
"(^\\d+(\\-\\d+)?$)|(.+)": {
"type": "object",
"description": "A port, range of ports (ex. \"40000-55000\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression.",
"properties": {
"onAutoForward": {
"type": "string",
"enum": [
"notify",
"openBrowser",
"openPreview",
"silent",
"ignore"
],
"enumDescriptions": [
"Shows a notification when a port is automatically forwarded.",
"Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.",
"Opens a preview in the same window when the port is automatically forwarded.",
"Shows no notification and takes no action when this port is automatically forwarded.",
"This port will not be automatically forwarded."
],
"description": "Defines the action that occurs when the port is discovered for automatic forwarding",
"default": "notify"
},
"elevateIfNeeded": {
"type": "boolean",
"description": "Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port.",
"default": false
},
"label": {
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
}
},
"default": {
"label": "Application",
"onAutoForward": "notify"
}
}
},
"markdownDescription": "Set default properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Application\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n},\n\".+\\\\/server.js\": {\n \"onAutoForward\": \"openPreview\"\n}\n```",
"defaultSnippets": [
{
"body": {
"${1:3000}": {
"label": "${2:Application}",
"onAutoForward": "notify"
}
}
}
],
"additionalProperties": false
},
"otherPortsAttributes": {
"type": "object",
"properties": {
"onAutoForward": {
"type": "string",
"enum": ["notify", "openBrowser", "openPreview", "silent", "ignore"],
"enumDescriptions": [
"Shows a notification when a port is automatically forwarded.",
"Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.",
"Opens a preview in the same window when the port is automatically forwarded.",
"Shows no notification and takes no action when this port is automatically forwarded.",
"This port will not be automatically forwarded."
],
"description": "Defines the action that occurs when the port is discovered for automatic forwarding",
"default": "notify"
},
"elevateIfNeeded": {
"type": "boolean",
"description": "Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port.",
"default": false
},
"label": {
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
}
},
"defaultSnippets": [{ "body": { "onAutoForward": "ignore" } }],
"markdownDescription": "Set default properties that are applied to all ports that don't get properties from the setting `remote.portsAttributes`. For example:\n\n```\n{\n \"onAutoForward\": \"ignore\"\n}\n```",
"additionalProperties": false
},
"settings": {
"$ref": "vscode://schemas/settings/machine",
"description": "Machine specific settings that should be copied into the container. These are only copied when connecting to the container for the first time."

View File

@@ -127,6 +127,106 @@
"minimum": 0
}
},
"portsAttributes": {
"type": "object",
"patternProperties": {
"(^\\d+(\\-\\d+)?$)|(.+)": {
"type": "object",
"description": "A port, range of ports (ex. \"40000-55000\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression.",
"properties": {
"onAutoForward": {
"type": "string",
"enum": [
"notify",
"openBrowser",
"openPreview",
"silent",
"ignore"
],
"enumDescriptions": [
"Shows a notification when a port is automatically forwarded.",
"Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.",
"Opens a preview in the same window when the port is automatically forwarded.",
"Shows no notification and takes no action when this port is automatically forwarded.",
"This port will not be automatically forwarded."
],
"description": "Defines the action that occurs when the port is discovered for automatic forwarding",
"default": "notify"
},
"elevateIfNeeded": {
"type": "boolean",
"description": "Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port.",
"default": false
},
"label": {
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
}
},
"default": {
"label": "Application",
"onAutoForward": "notify"
},
"additionalProperties": false
}
},
"markdownDescription": "Set default properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Application\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n},\n\".+\\\\/server.js\": {\n \"onAutoForward\": \"openPreview\"\n}\n```",
"defaultSnippets": [
{
"body": {
"${1:3000}": {
"label": "${2:Application}",
"onAutoForward": "notify"
}
}
}
],
"additionalProperties": false
},
"otherPortsAttributes": {
"type": "object",
"properties": {
"onAutoForward": {
"type": "string",
"enum": [
"notify",
"openBrowser",
"openPreview",
"silent",
"ignore"
],
"enumDescriptions": [
"Shows a notification when a port is automatically forwarded.",
"Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.",
"Opens a preview in the same window when the port is automatically forwarded.",
"Shows no notification and takes no action when this port is automatically forwarded.",
"This port will not be automatically forwarded."
],
"description": "Defines the action that occurs when the port is discovered for automatic forwarding",
"default": "notify"
},
"elevateIfNeeded": {
"type": "boolean",
"description": "Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port.",
"default": false
},
"label": {
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
}
},
"defaultSnippets": [
{
"body": {
"onAutoForward": "ignore"
}
}
],
"markdownDescription": "Set default properties that are applied to all ports that don't get properties from the setting `remote.portsAttributes`. For example:\n\n```\n{\n \"onAutoForward\": \"ignore\"\n}\n```",
"additionalProperties": false
},
"remoteEnv": {
"type": "object",
"additionalProperties": {
@@ -326,6 +426,106 @@
"minimum": 0
}
},
"portsAttributes": {
"type": "object",
"patternProperties": {
"(^\\d+(\\-\\d+)?$)|(.+)": {
"type": "object",
"description": "A port, range of ports (ex. \"40000-55000\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression.",
"properties": {
"onAutoForward": {
"type": "string",
"enum": [
"notify",
"openBrowser",
"openPreview",
"silent",
"ignore"
],
"enumDescriptions": [
"Shows a notification when a port is automatically forwarded.",
"Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.",
"Opens a preview in the same window when the port is automatically forwarded.",
"Shows no notification and takes no action when this port is automatically forwarded.",
"This port will not be automatically forwarded."
],
"description": "Defines the action that occurs when the port is discovered for automatic forwarding",
"default": "notify"
},
"elevateIfNeeded": {
"type": "boolean",
"description": "Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port.",
"default": false
},
"label": {
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
}
},
"default": {
"label": "Application",
"onAutoForward": "notify"
},
"additionalProperties": false
}
},
"markdownDescription": "Set default properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Application\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n},\n\".+\\\\/server.js\": {\n \"onAutoForward\": \"openPreview\"\n}\n```",
"defaultSnippets": [
{
"body": {
"${1:3000}": {
"label": "${2:Application}",
"onAutoForward": "notify"
}
}
}
],
"additionalProperties": false
},
"otherPortsAttributes": {
"type": "object",
"properties": {
"onAutoForward": {
"type": "string",
"enum": [
"notify",
"openBrowser",
"openPreview",
"silent",
"ignore"
],
"enumDescriptions": [
"Shows a notification when a port is automatically forwarded.",
"Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.",
"Opens a preview in the same window when the port is automatically forwarded.",
"Shows no notification and takes no action when this port is automatically forwarded.",
"This port will not be automatically forwarded."
],
"description": "Defines the action that occurs when the port is discovered for automatic forwarding",
"default": "notify"
},
"elevateIfNeeded": {
"type": "boolean",
"description": "Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port.",
"default": false
},
"label": {
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
}
},
"defaultSnippets": [
{
"body": {
"onAutoForward": "ignore"
}
}
],
"markdownDescription": "Set default properties that are applied to all ports that don't get properties from the setting `remote.portsAttributes`. For example:\n\n```\n{\n \"onAutoForward\": \"ignore\"\n}\n```",
"additionalProperties": false
},
"remoteEnv": {
"type": "object",
"additionalProperties": {
@@ -501,6 +701,106 @@
"minimum": 0
}
},
"portsAttributes": {
"type": "object",
"patternProperties": {
"(^\\d+(\\-\\d+)?$)|(.+)": {
"type": "object",
"description": "A port, range of ports (ex. \"40000-55000\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression.",
"properties": {
"onAutoForward": {
"type": "string",
"enum": [
"notify",
"openBrowser",
"openPreview",
"silent",
"ignore"
],
"enumDescriptions": [
"Shows a notification when a port is automatically forwarded.",
"Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.",
"Opens a preview in the same window when the port is automatically forwarded.",
"Shows no notification and takes no action when this port is automatically forwarded.",
"This port will not be automatically forwarded."
],
"description": "Defines the action that occurs when the port is discovered for automatic forwarding",
"default": "notify"
},
"elevateIfNeeded": {
"type": "boolean",
"description": "Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port.",
"default": false
},
"label": {
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
}
},
"default": {
"label": "Application",
"onAutoForward": "notify"
},
"additionalProperties": false
}
},
"markdownDescription": "Set default properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Application\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n},\n\".+\\\\/server.js\": {\n \"onAutoForward\": \"openPreview\"\n}\n```",
"defaultSnippets": [
{
"body": {
"${1:3000}": {
"label": "${2:Application}",
"onAutoForward": "notify"
}
}
}
],
"additionalProperties": false
},
"otherPortsAttributes": {
"type": "object",
"properties": {
"onAutoForward": {
"type": "string",
"enum": [
"notify",
"openBrowser",
"openPreview",
"silent",
"ignore"
],
"enumDescriptions": [
"Shows a notification when a port is automatically forwarded.",
"Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.",
"Opens a preview in the same window when the port is automatically forwarded.",
"Shows no notification and takes no action when this port is automatically forwarded.",
"This port will not be automatically forwarded."
],
"description": "Defines the action that occurs when the port is discovered for automatic forwarding",
"default": "notify"
},
"elevateIfNeeded": {
"type": "boolean",
"description": "Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port.",
"default": false
},
"label": {
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
}
},
"defaultSnippets": [
{
"body": {
"onAutoForward": "ignore"
}
}
],
"markdownDescription": "Set default properties that are applied to all ports that don't get properties from the setting `remote.portsAttributes`. For example:\n\n```\n{\n \"onAutoForward\": \"ignore\"\n}\n```",
"additionalProperties": false
},
"remoteEnv": {
"type": "object",
"additionalProperties": {
@@ -642,6 +942,106 @@
"minimum": 0
}
},
"portsAttributes": {
"type": "object",
"patternProperties": {
"(^\\d+(\\-\\d+)?$)|(.+)": {
"type": "object",
"description": "A port, range of ports (ex. \"40000-55000\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression.",
"properties": {
"onAutoForward": {
"type": "string",
"enum": [
"notify",
"openBrowser",
"openPreview",
"silent",
"ignore"
],
"enumDescriptions": [
"Shows a notification when a port is automatically forwarded.",
"Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.",
"Opens a preview in the same window when the port is automatically forwarded.",
"Shows no notification and takes no action when this port is automatically forwarded.",
"This port will not be automatically forwarded."
],
"description": "Defines the action that occurs when the port is discovered for automatic forwarding",
"default": "notify"
},
"elevateIfNeeded": {
"type": "boolean",
"description": "Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port.",
"default": false
},
"label": {
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
}
},
"default": {
"label": "Application",
"onAutoForward": "notify"
},
"additionalProperties": false
}
},
"markdownDescription": "Set default properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Application\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n},\n\".+\\\\/server.js\": {\n \"onAutoForward\": \"openPreview\"\n}\n```",
"defaultSnippets": [
{
"body": {
"${1:3000}": {
"label": "${2:Application}",
"onAutoForward": "notify"
}
}
}
],
"additionalProperties": false
},
"otherPortsAttributes": {
"type": "object",
"properties": {
"onAutoForward": {
"type": "string",
"enum": [
"notify",
"openBrowser",
"openPreview",
"silent",
"ignore"
],
"enumDescriptions": [
"Shows a notification when a port is automatically forwarded.",
"Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.",
"Opens a preview in the same window when the port is automatically forwarded.",
"Shows no notification and takes no action when this port is automatically forwarded.",
"This port will not be automatically forwarded."
],
"description": "Defines the action that occurs when the port is discovered for automatic forwarding",
"default": "notify"
},
"elevateIfNeeded": {
"type": "boolean",
"description": "Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port.",
"default": false
},
"label": {
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
}
},
"defaultSnippets": [
{
"body": {
"onAutoForward": "ignore"
}
}
],
"markdownDescription": "Set default properties that are applied to all ports that don't get properties from the setting `remote.portsAttributes`. For example:\n\n```\n{\n \"onAutoForward\": \"ignore\"\n}\n```",
"additionalProperties": false
},
"remoteEnv": {
"type": "object",
"additionalProperties": {
@@ -752,6 +1152,106 @@
"minimum": 0
}
},
"portsAttributes": {
"type": "object",
"patternProperties": {
"(^\\d+(\\-\\d+)?$)|(.+)": {
"type": "object",
"description": "A port, range of ports (ex. \"40000-55000\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression.",
"properties": {
"onAutoForward": {
"type": "string",
"enum": [
"notify",
"openBrowser",
"openPreview",
"silent",
"ignore"
],
"enumDescriptions": [
"Shows a notification when a port is automatically forwarded.",
"Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.",
"Opens a preview in the same window when the port is automatically forwarded.",
"Shows no notification and takes no action when this port is automatically forwarded.",
"This port will not be automatically forwarded."
],
"description": "Defines the action that occurs when the port is discovered for automatic forwarding",
"default": "notify"
},
"elevateIfNeeded": {
"type": "boolean",
"description": "Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port.",
"default": false
},
"label": {
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
}
},
"default": {
"label": "Application",
"onAutoForward": "notify"
},
"additionalProperties": false
}
},
"markdownDescription": "Set default properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Application\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n},\n\".+\\\\/server.js\": {\n \"onAutoForward\": \"openPreview\"\n}\n```",
"defaultSnippets": [
{
"body": {
"${1:3000}": {
"label": "${2:Application}",
"onAutoForward": "notify"
}
}
}
],
"additionalProperties": false
},
"otherPortsAttributes": {
"type": "object",
"properties": {
"onAutoForward": {
"type": "string",
"enum": [
"notify",
"openBrowser",
"openPreview",
"silent",
"ignore"
],
"enumDescriptions": [
"Shows a notification when a port is automatically forwarded.",
"Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.",
"Opens a preview in the same window when the port is automatically forwarded.",
"Shows no notification and takes no action when this port is automatically forwarded.",
"This port will not be automatically forwarded."
],
"description": "Defines the action that occurs when the port is discovered for automatic forwarding",
"default": "notify"
},
"elevateIfNeeded": {
"type": "boolean",
"description": "Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port.",
"default": false
},
"label": {
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
}
},
"defaultSnippets": [
{
"body": {
"onAutoForward": "ignore"
}
}
],
"markdownDescription": "Set default properties that are applied to all ports that don't get properties from the setting `remote.portsAttributes`. For example:\n\n```\n{\n \"onAutoForward\": \"ignore\"\n}\n```",
"additionalProperties": false
},
"remoteEnv": {
"type": "object",
"additionalProperties": {

View File

@@ -33,6 +33,105 @@
"minimum": 0
}
},
"portsAttributes": {
"type": "object",
"patternProperties": {
"(^\\d+(\\-\\d+)?$)|(.+)": {
"type": "object",
"description": "A port, range of ports (ex. \"40000-55000\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression.",
"properties": {
"onAutoForward": {
"type": "string",
"enum": [
"notify",
"openBrowser",
"openPreview",
"silent",
"ignore"
],
"enumDescriptions": [
"Shows a notification when a port is automatically forwarded.",
"Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.",
"Opens a preview in the same window when the port is automatically forwarded.",
"Shows no notification and takes no action when this port is automatically forwarded.",
"This port will not be automatically forwarded."
],
"description": "Defines the action that occurs when the port is discovered for automatic forwarding",
"default": "notify"
},
"elevateIfNeeded": {
"type": "boolean",
"description": "Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port.",
"default": false
},
"label": {
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
}
},
"default": {
"label": "Application",
"onAutoForward": "notify"
}
}
},
"markdownDescription": "Set default properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Application\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n},\n\".+\\\\/server.js\": {\n \"onAutoForward\": \"openPreview\"\n}\n```",
"defaultSnippets": [
{
"body": {
"${1:3000}": {
"label": "${2:Application}",
"onAutoForward": "notify"
}
}
}
],
"additionalProperties": false
},
"otherPortsAttributes": {
"type": "object",
"properties": {
"onAutoForward": {
"type": "string",
"enum": [
"notify",
"openBrowser",
"openPreview",
"silent",
"ignore"
],
"enumDescriptions": [
"Shows a notification when a port is automatically forwarded.",
"Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.",
"Opens a preview in the same window when the port is automatically forwarded.",
"Shows no notification and takes no action when this port is automatically forwarded.",
"This port will not be automatically forwarded."
],
"description": "Defines the action that occurs when the port is discovered for automatic forwarding",
"default": "notify"
},
"elevateIfNeeded": {
"type": "boolean",
"description": "Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port.",
"default": false
},
"label": {
"type": "string",
"description": "Label that will be shown in the UI for this port.",
"default": "Application"
}
},
"defaultSnippets": [
{
"body": {
"onAutoForward": "ignore"
}
}
],
"markdownDescription": "Set default properties that are applied to all ports that don't get properties from the setting `remote.portsAttributes`. For example:\n\n```\n{\n \"onAutoForward\": \"ignore\"\n}\n```",
"additionalProperties": false
},
"remoteEnv": {
"type": "object",
"additionalProperties": {
@@ -205,7 +304,7 @@
"$ref": "#/definitions/buildOptions"
}
]
}
}
},
"required": [
"build"

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { getLocation, parse, visit } from 'jsonc-parser';
import { getLocation, JSONPath, parse, visit } from 'jsonc-parser';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { SettingsDocument } from './settingsDocumentHelper';
@@ -22,6 +22,9 @@ export function activate(context: vscode.ExtensionContext): void {
// task.json variable suggestions
context.subscriptions.push(registerVariableCompletions('**/tasks.json'));
// keybindings.json/package.json context key suggestions
context.subscriptions.push(registerContextKeyCompletions());
}
function registerSettingsCompletions(): vscode.Disposable {
@@ -136,3 +139,83 @@ vscode.languages.registerDocumentSymbolProvider({ pattern: '**/launch.json', lan
return result;
}
}, { label: 'Launch Targets' });
function registerContextKeyCompletions(): vscode.Disposable {
type ContextKeyInfo = { key: string, type?: string, description?: string };
const paths = new Map<vscode.DocumentFilter, JSONPath[]>([
[{ language: 'jsonc', pattern: '**/keybindings.json' }, [
['*', 'when']
]],
[{ language: 'json', pattern: '**/package.json' }, [
['contributes', 'menus', '*', '*', 'when'],
['contributes', 'views', '*', '*', 'when'],
['contributes', 'viewsWelcome', '*', 'when'],
['contributes', 'keybindings', '*', 'when'],
['contributes', 'keybindings', 'when'],
]]
]);
return vscode.languages.registerCompletionItemProvider(
[...paths.keys()],
{
async provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken) {
const location = getLocation(document.getText(), document.offsetAt(position));
if (location.isAtPropertyKey) {
return;
}
let isValidLocation = false;
for (const [key, value] of paths) {
if (vscode.languages.match(key, document)) {
if (value.some(location.matches.bind(location))) {
isValidLocation = true;
break;
}
}
}
if (!isValidLocation) {
return;
}
// for JSON everything with quotes is a word
const jsonWord = document.getWordRangeAtPosition(position);
if (!jsonWord || jsonWord.start.isEqual(position) || jsonWord.end.isEqual(position)) {
// we aren't inside a "JSON word" or on its quotes
return;
}
let replacing: vscode.Range | undefined;
if (jsonWord.end.character - jsonWord.start.character === 2 || document.getWordRangeAtPosition(position, /\s+/)) {
// empty json word or on whitespace
replacing = new vscode.Range(position, position);
} else {
replacing = document.getWordRangeAtPosition(position, /[a-zA-Z.]+/);
}
if (!replacing) {
return;
}
const inserting = replacing.with(undefined, position);
const data = await vscode.commands.executeCommand<ContextKeyInfo[]>('getContextKeyInfo');
if (token.isCancellationRequested || !data) {
return;
}
const result = new vscode.CompletionList();
for (const item of data) {
const completion = new vscode.CompletionItem(item.key, vscode.CompletionItemKind.Constant);
completion.detail = item.type;
completion.range = { replacing, inserting };
completion.documentation = item.description;
result.items.push(completion);
}
return result;
}
}
);
}

View File

@@ -33,3 +33,28 @@ export function provideInstalledExtensionProposals(existing: string[], additiona
return undefined;
}
export function provideWorkspaceTrustExtensionProposals(existing: string[], range: vscode.Range): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList> {
if (Array.isArray(existing)) {
const extensions = vscode.extensions.all.filter(e => e.packageJSON.main);
const extensionProposals = extensions.filter(e => existing.indexOf(e.id) === -1);
if (extensionProposals.length) {
return extensionProposals.map(e => {
const item = new vscode.CompletionItem(e.id);
const insertText = `"${e.id}": {\n\t"supported": false,\n\t"version": "${e.packageJSON.version}"\n}`;
item.kind = vscode.CompletionItemKind.Value;
item.insertText = insertText;
item.range = range;
item.filterText = insertText;
return item;
});
} else {
const example = new vscode.CompletionItem(localize('exampleExtension', "Example"));
example.insertText = '"vscode.csharp: {\n\t"supported": false,\n\t"version": "0.0.0"\n}`;"';
example.kind = vscode.CompletionItemKind.Value;
example.range = range;
return [example];
}
}
return undefined;
}

View File

@@ -6,7 +6,7 @@
import * as vscode from 'vscode';
import { getLocation, Location, parse } from 'jsonc-parser';
import * as nls from 'vscode-nls';
import { provideInstalledExtensionProposals } from './extensionsProposals';
import { provideInstalledExtensionProposals, provideWorkspaceTrustExtensionProposals } from './extensionsProposals';
const localize = nls.loadMessageBundle();
@@ -60,6 +60,15 @@ export class SettingsDocument {
return provideInstalledExtensionProposals(alreadyConfigured, `: [\n\t"ui"\n]`, range, true);
}
// extensions.supportUntrustedWorkspaces
if (location.path[0] === 'extensions.supportUntrustedWorkspaces' && location.path.length === 2 && location.isAtPropertyKey) {
let alreadyConfigured: string[] = [];
try {
alreadyConfigured = Object.keys(parse(this.document.getText())['extensions.supportUntrustedWorkspaces']);
} catch (e) {/* ignore error */ }
return provideWorkspaceTrustExtensionProposals(alreadyConfigured, range);
}
return this.provideLanguageOverridesCompletionItems(location, position);
}

View File

@@ -1,5 +1,5 @@
{
"extends": "../shared.tsconfig.json",
"extends": "../tsconfig.base.json",
"compilerOptions": {
"outDir": "./out"
},

View File

@@ -39,7 +39,8 @@ export function createContext(): TestContext {
globalStorageUri: undefined,
logUri: undefined,
storageUri: undefined,
secrets: undefined
secrets: undefined,
extension: undefined
},
viewContext: viewContext
};

View File

@@ -48,7 +48,7 @@ export class DacFxSummaryPage extends BasePage {
return true;
}
async onPageLeave(): Promise<boolean> {
override async onPageLeave(): Promise<boolean> {
this.instance.wizard.generateScriptButton.hidden = true;
return true;
}

View File

@@ -160,7 +160,7 @@ export class DeployConfigPage extends DacFxConfigPage {
};
}
protected async populateDatabaseDropdown(): Promise<boolean> {
protected override async populateDatabaseDropdown(): Promise<boolean> {
this.databaseLoader.loading = true;
this.databaseDropdown.updateProperties({ values: [] });
if (!this.model.server) {

View File

@@ -286,7 +286,7 @@ export class DeployPlanPage extends DacFxConfigPage {
};
}
public setupNavigationValidator() {
public override setupNavigationValidator() {
this.instance.registerNavigationValidator(() => {
return true;
});

View File

@@ -44,12 +44,12 @@ export class ExportConfigPage extends DacFxConfigPage {
}
async onPageLeave(): Promise<boolean> {
override async onPageLeave(): Promise<boolean> {
this.appendFileExtensionIfNeeded();
return true;
}
public setupNavigationValidator() {
public override setupNavigationValidator() {
this.instance.registerNavigationValidator(() => {
if (this.databaseLoader.loading) {
return false;

View File

@@ -46,12 +46,12 @@ export class ExtractConfigPage extends DacFxConfigPage {
return r1 && r2;
}
async onPageLeave(): Promise<boolean> {
override async onPageLeave(): Promise<boolean> {
this.appendFileExtensionIfNeeded();
return true;
}
public setupNavigationValidator(): void {
public override setupNavigationValidator(): void {
this.instance.registerNavigationValidator(() => {
if (this.databaseLoader.loading) {
return false;

View File

@@ -1,5 +1,5 @@
{
"extends": "../shared.tsconfig.json",
"extends": "../tsconfig.base.json",
"compilerOptions": {
"outDir": "./out",
"strict": false,

View File

@@ -62,7 +62,7 @@ export class NewProjectDialog extends DialogBase {
}
}
async onComplete(): Promise<void> {
override async onComplete(): Promise<void> {
try {
const validateWorkspace = await this.workspaceService.validateWorkspace();

View File

@@ -92,7 +92,7 @@ export class OpenExistingDialog extends DialogBase {
}
}
async onComplete(): Promise<void> {
override async onComplete(): Promise<void> {
try {
if (this.targetTypeRadioCardGroup?.selectedCardId === constants.Workspace) {
// capture that workspace was selected, also if there's already an open workspace that's being replaced

View File

@@ -1,5 +1,5 @@
{
"extends": "../shared.tsconfig.json",
"extends": "../tsconfig.base.json",
"compilerOptions": {
"outDir": "./out",
"noUnusedParameters": false

View File

@@ -9,7 +9,7 @@
"vscode": "*"
},
"scripts": {
"update-grammar": "node ../node_modules/.bin/vscode-grammar-updater moby/moby contrib/syntax/textmate/Docker.tmbundle/Syntaxes/Dockerfile.tmLanguage ./syntaxes/docker.tmLanguage.json"
"update-grammar": "node ../node_modules/vscode-grammar-updater/bin moby/moby contrib/syntax/textmate/Docker.tmbundle/Syntaxes/Dockerfile.tmLanguage ./syntaxes/docker.tmLanguage.json"
},
"contributes": {
"languages": [

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -8,6 +8,7 @@
"engines": {
"vscode": "^1.4.0"
},
"icon": "images/icon.png",
"activationEvents": [
"onLanguage:json",
"onLanguage:markdown",
@@ -15,13 +16,19 @@
],
"main": "./out/extensionEditingMain",
"browser": "./dist/browser/extensionEditingBrowserMain",
"capabilities": {
"virtualWorkspaces": true,
"untrustedWorkspaces": {
"supported": true
}
},
"scripts": {
"compile": "gulp compile-extension:extension-editing",
"watch": "gulp watch-extension:extension-editing"
},
"dependencies": {
"jsonc-parser": "^2.2.1",
"markdown-it": "^8.3.1",
"markdown-it": "^12.0.4",
"parse5": "^3.0.2",
"vscode-nls": "^4.1.1"
},

View File

@@ -1,5 +1,5 @@
{
"extends": "../shared.tsconfig.json",
"extends": "../tsconfig.base.json",
"compilerOptions": {
"outDir": "./out",
"typeRoots": [
@@ -9,4 +9,4 @@
"include": [
"src/**/*"
]
}
}

View File

@@ -17,40 +17,38 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.78.tgz#5d4a3f579c1524e01ee21bf474e6fba09198f470"
integrity sha512-+vD6E8ixntRzzZukoF3uP1iV+ZjVN3koTcaeK+BEoc/kSfGbLDIGC7RmCaUgVpUfN6cWvfczFRERCyKM9mkvXg==
argparse@^1.0.7:
version "1.0.9"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86"
integrity sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=
dependencies:
sprintf-js "~1.0.2"
argparse@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
entities@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
integrity sha1-blwtClYhtdra7O+AuQ7ftc13cvA=
entities@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==
jsonc-parser@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.1.tgz#db73cd59d78cce28723199466b2a03d1be1df2bc"
integrity sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w==
linkify-it@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.0.3.tgz#d94a4648f9b1c179d64fa97291268bdb6ce9434f"
integrity sha1-2UpGSPmxwXnWT6lykSaL22zpQ08=
linkify-it@^3.0.1:
version "3.0.2"
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-3.0.2.tgz#f55eeb8bc1d3ae754049e124ab3bb56d97797fb8"
integrity sha512-gDBO4aHNZS6coiZCKVhSNh43F9ioIL4JwRjLZPkoLIY4yZFwg264Y5lu2x6rb1Js42Gh6Yqm2f6L2AJcnkzinQ==
dependencies:
uc.micro "^1.0.1"
markdown-it@^8.3.1:
version "8.4.0"
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.0.tgz#e2400881bf171f7018ed1bd9da441dac8af6306d"
integrity sha512-tNuOCCfunY5v5uhcO2AUMArvKAyKMygX8tfup/JrgnsDqcCATQsAExBq7o5Ml9iMmO82bk6jYNLj6khcrl0JGA==
markdown-it@^12.0.4:
version "12.0.4"
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.0.4.tgz#eec8247d296327eac3ba9746bdeec9cfcc751e33"
integrity sha512-34RwOXZT8kyuOJy25oJNJoulO8L0bTHYWXcdZBYZqFnjIy3NgjeoM3FmPXIOFQ26/lSHYMr8oc62B6adxXcb3Q==
dependencies:
argparse "^1.0.7"
entities "~1.1.1"
linkify-it "^2.0.0"
argparse "^2.0.1"
entities "~2.1.0"
linkify-it "^3.0.1"
mdurl "^1.0.1"
uc.micro "^1.0.3"
uc.micro "^1.0.5"
mdurl@^1.0.1:
version "1.0.1"
@@ -64,16 +62,16 @@ parse5@^3.0.2:
dependencies:
"@types/node" "^6.0.46"
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
uc.micro@^1.0.1, uc.micro@^1.0.3:
uc.micro@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.3.tgz#7ed50d5e0f9a9fb0a573379259f2a77458d50192"
integrity sha1-ftUNXg+an7ClczeSWfKndFjVAZI=
uc.micro@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
vscode-nls@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c"

View File

@@ -1,8 +0,0 @@
src/**
test/**
out/**
tsconfig.json
build/**
extension.webpack.config.js
cgmanifest.json
yarn.lock

View File

@@ -1,7 +0,0 @@
# Git UI integration for Visual Studio Code
**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled.
## Features
See [Git support in VS Code](https://code.visualstudio.com/docs/editor/versioncontrol#_git-support) to learn about the features of this extension.

View File

@@ -1,4 +0,0 @@
{
"registrations": [],
"version": 1
}

View File

@@ -1,35 +0,0 @@
{
"name": "git-ui",
"displayName": "%displayName%",
"description": "%description%",
"publisher": "vscode",
"license": "MIT",
"version": "1.0.0",
"engines": {
"vscode": "^1.5.0"
},
"extensionKind": [
"ui"
],
"aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
"enableProposedApi": true,
"categories": [
"Other"
],
"activationEvents": [
"onCommand:git.credential"
],
"main": "./out/main",
"icon": "resources/icons/git.png",
"scripts": {
"compile": "gulp compile-extension:git-ui",
"watch": "gulp watch-extension:git-ui"
},
"devDependencies": {
"@types/node": "^12.19.9"
},
"repository": {
"type": "git",
"url": "https://github.com/microsoft/vscode.git"
}
}

View File

@@ -1,4 +0,0 @@
{
"displayName": "Git UI",
"description": "Git SCM UI Integration"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -1,58 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtensionContext, commands } from 'vscode';
import * as cp from 'child_process';
export async function deactivate(): Promise<any> {
}
export async function activate(context: ExtensionContext): Promise<void> {
context.subscriptions.push(commands.registerCommand('git.credential', async (_data: any) => {
return { stdout: '', stderr: '', code: 0 };
// try {
// const { stdout, stderr } = await exec(`git credential ${data.command}`, {
// stdin: data.stdin,
// env: Object.assign(process.env, { GIT_TERMINAL_PROMPT: '0' })
// });
// return { stdout, stderr, code: 0 };
// } catch ({ stdout, stderr, error }) {
// const code = error.code || 0;
// if (stderr.indexOf('terminal prompts disabled') !== -1) {
// stderr = '';
// }
// return { stdout, stderr, code };
// }
}));
}
export interface ExecResult {
error: Error | null;
stdout: string;
stderr: string;
}
export function exec(command: string, options: cp.ExecOptions & { stdin?: string } = {}) {
return new Promise<ExecResult>((resolve, reject) => {
const child = cp.exec(command, options, (error, stdout, stderr) => {
(error ? reject : resolve)({ error, stdout, stderr });
});
if (options.stdin) {
child.stdin!.write(options.stdin, (err: any) => {
if (err) {
reject(err);
return;
}
child.stdin!.end((err: any) => {
if (err) {
reject(err);
}
});
});
}
});
}

View File

@@ -1,8 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/// <reference path='../../../../src/vs/vscode.d.ts'/>
/// <reference path='../../../../src/vs/vscode.proposed.d.ts'/>
/// <reference types='@types/node'/>

View File

@@ -1,13 +0,0 @@
{
"extends": "../shared.tsconfig.json",
"compilerOptions": {
"outDir": "./out",
"experimentalDecorators": true,
"typeRoots": [
"./node_modules/@types"
]
},
"include": [
"src/**/*"
]
}

View File

@@ -1,8 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@types/node@^12.19.9":
version "12.19.9"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.9.tgz#990ad687ad8b26ef6dcc34a4f69c33d40c95b679"
integrity sha512-yj0DOaQeUrk3nJ0bd3Y5PeDRJ6W0r+kilosLA+dzF3dola/o9hxhMSg2sFvVcA2UHS5JSOsZp4S0c1OEXc4m1Q==

View File

@@ -26,6 +26,12 @@
"update-grammar": "node ./build/update-grammars.js",
"test": "node ../../node_modules/mocha/bin/mocha"
},
"capabilities": {
"virtualWorkspaces": true,
"untrustedWorkspaces": {
"supported": true
}
},
"contributes": {
"commands": [
{
@@ -495,6 +501,16 @@
"title": "%command.timelineCopyCommitMessage%",
"category": "Git"
},
{
"command": "git.timeline.selectForCompare",
"title": "%command.timelineSelectForCompare%",
"category": "Git"
},
{
"command": "git.timeline.compareWithSelected",
"title": "%command.timelineCompareWithSelected%",
"category": "Git"
},
{
"command": "git.rebaseAbort",
"title": "%command.rebaseAbort%",
@@ -874,6 +890,14 @@
{
"command": "git.timeline.copyCommitMessage",
"when": "false"
},
{
"command": "git.timeline.selectForCompare",
"when": "false"
},
{
"command": "git.timeline.compareWithSelected",
"when": "false"
}
],
"scm/title": [
@@ -964,7 +988,7 @@
{
"command": "git.stageAllMerge",
"when": "scmProvider == git && scmResourceGroup == merge",
"group": "inline"
"group": "inline@2"
},
{
"command": "git.unstageAll",
@@ -974,7 +998,7 @@
{
"command": "git.unstageAll",
"when": "scmProvider == git && scmResourceGroup == index",
"group": "inline"
"group": "inline@2"
},
{
"command": "git.cleanAll",
@@ -989,12 +1013,12 @@
{
"command": "git.cleanAll",
"when": "scmProvider == git && scmResourceGroup == workingTree && config.git.untrackedChanges == mixed",
"group": "inline"
"group": "inline@2"
},
{
"command": "git.stageAll",
"when": "scmProvider == git && scmResourceGroup == workingTree && config.git.untrackedChanges == mixed",
"group": "inline"
"group": "inline@2"
},
{
"command": "git.cleanAllTracked",
@@ -1009,12 +1033,12 @@
{
"command": "git.cleanAllTracked",
"when": "scmProvider == git && scmResourceGroup == workingTree && config.git.untrackedChanges != mixed",
"group": "inline"
"group": "inline@2"
},
{
"command": "git.stageAllTracked",
"when": "scmProvider == git && scmResourceGroup == workingTree && config.git.untrackedChanges != mixed",
"group": "inline"
"group": "inline@2"
},
{
"command": "git.cleanAllUntracked",
@@ -1029,12 +1053,12 @@
{
"command": "git.cleanAllUntracked",
"when": "scmProvider == git && scmResourceGroup == untracked",
"group": "inline"
"group": "inline@2"
},
{
"command": "git.stageAllUntracked",
"when": "scmProvider == git && scmResourceGroup == untracked",
"group": "inline"
"group": "inline@2"
}
],
"scm/resourceFolder/context": [
@@ -1046,7 +1070,7 @@
{
"command": "git.stage",
"when": "scmProvider == git && scmResourceGroup == merge",
"group": "inline"
"group": "inline@2"
},
{
"command": "git.unstage",
@@ -1056,7 +1080,7 @@
{
"command": "git.unstage",
"when": "scmProvider == git && scmResourceGroup == index",
"group": "inline"
"group": "inline@2"
},
{
"command": "git.stage",
@@ -1071,12 +1095,12 @@
{
"command": "git.clean",
"when": "scmProvider == git && scmResourceGroup == workingTree",
"group": "inline"
"group": "inline@2"
},
{
"command": "git.stage",
"when": "scmProvider == git && scmResourceGroup == workingTree",
"group": "inline"
"group": "inline@2"
},
{
"command": "git.ignore",
@@ -1091,7 +1115,7 @@
{
"command": "git.stage",
"when": "scmProvider == git && scmResourceGroup == untracked",
"group": "inline"
"group": "inline@2"
},
{
"command": "git.clean",
@@ -1101,7 +1125,7 @@
{
"command": "git.clean",
"when": "scmProvider == git && scmResourceGroup == untracked",
"group": "inline"
"group": "inline@2"
},
{
"command": "git.ignore",
@@ -1123,7 +1147,7 @@
{
"command": "git.stage",
"when": "scmProvider == git && scmResourceGroup == merge",
"group": "inline"
"group": "inline@2"
},
{
"command": "git.revealInExplorer",
@@ -1133,12 +1157,12 @@
{
"command": "git.openFile2",
"when": "scmProvider == git && scmResourceGroup == merge && config.git.showInlineOpenFileAction && config.git.openDiffOnClick",
"group": "inline0"
"group": "inline@1"
},
{
"command": "git.openChange",
"when": "scmProvider == git && scmResourceGroup == merge && config.git.showInlineOpenFileAction && !config.git.openDiffOnClick",
"group": "inline0"
"group": "inline@1"
},
{
"command": "git.openChange",
@@ -1163,7 +1187,7 @@
{
"command": "git.unstage",
"when": "scmProvider == git && scmResourceGroup == index",
"group": "inline"
"group": "inline@2"
},
{
"command": "git.revealInExplorer",
@@ -1173,12 +1197,12 @@
{
"command": "git.openFile2",
"when": "scmProvider == git && scmResourceGroup == index && config.git.showInlineOpenFileAction && config.git.openDiffOnClick",
"group": "inline0"
"group": "inline@1"
},
{
"command": "git.openChange",
"when": "scmProvider == git && scmResourceGroup == index && config.git.showInlineOpenFileAction && !config.git.openDiffOnClick",
"group": "inline0"
"group": "inline@1"
},
{
"command": "git.openChange",
@@ -1208,22 +1232,22 @@
{
"command": "git.clean",
"when": "scmProvider == git && scmResourceGroup == workingTree",
"group": "inline"
"group": "inline@2"
},
{
"command": "git.stage",
"when": "scmProvider == git && scmResourceGroup == workingTree",
"group": "inline"
"group": "inline@2"
},
{
"command": "git.openFile2",
"when": "scmProvider == git && scmResourceGroup == workingTree && config.git.showInlineOpenFileAction && config.git.openDiffOnClick",
"group": "inline0"
"group": "inline@1"
},
{
"command": "git.openChange",
"when": "scmProvider == git && scmResourceGroup == workingTree && config.git.showInlineOpenFileAction && !config.git.openDiffOnClick",
"group": "inline0"
"group": "inline@1"
},
{
"command": "git.ignore",
@@ -1263,22 +1287,22 @@
{
"command": "git.clean",
"when": "scmProvider == git && scmResourceGroup == untracked && !gitFreshRepository",
"group": "inline"
"group": "inline@2"
},
{
"command": "git.stage",
"when": "scmProvider == git && scmResourceGroup == untracked",
"group": "inline"
"group": "inline@2"
},
{
"command": "git.openFile2",
"when": "scmProvider == git && scmResourceGroup == untracked && config.git.showInlineOpenFileAction && config.git.openDiffOnClick",
"group": "inline0"
"group": "inline@1"
},
{
"command": "git.openChange",
"when": "scmProvider == git && scmResourceGroup == untracked && config.git.showInlineOpenFileAction && !config.git.openDiffOnClick",
"group": "inline0"
"group": "inline@1"
},
{
"command": "git.ignore",
@@ -1349,17 +1373,27 @@
{
"command": "git.timeline.openDiff",
"group": "1_actions",
"when": "config.git.enabled && !git.missing && timelineItem =~ /git:file\\b/"
"when": "config.git.enabled && !git.missing && timelineItem =~ /git:file\\b/ && !listMultiSelection"
},
{
"command": "git.timeline.compareWithSelected",
"group": "3_compare@1",
"when": "config.git.enabled && !git.missing && git.timeline.selectedForCompare && timelineItem =~ /git:file\\b/ && !listMultiSelection"
},
{
"command": "git.timeline.selectForCompare",
"group": "3_compare@2",
"when": "config.git.enabled && !git.missing && timelineItem =~ /git:file\\b/ && !listMultiSelection"
},
{
"command": "git.timeline.copyCommitId",
"group": "5_copy@1",
"when": "config.git.enabled && !git.missing && timelineItem =~ /git:file:commit\\b/"
"when": "config.git.enabled && !git.missing && timelineItem =~ /git:file:commit\\b/ && !listMultiSelection"
},
{
"command": "git.timeline.copyCommitMessage",
"group": "5_copy@2",
"when": "config.git.enabled && !git.missing && timelineItem =~ /git:file:commit\\b/"
"when": "config.git.enabled && !git.missing && timelineItem =~ /git:file:commit\\b/ && !listMultiSelection"
}
],
"git.commit": [
@@ -1519,13 +1553,17 @@
"command": "git.branchFrom",
"group": "branch@4"
},
{
"command": "git.renameBranch",
"group": "branch@5"
},
{
"command": "git.renameBranch",
"group": "branch@5"
"command": "git.deleteBranch",
"group": "branch@6"
},
{
"command": "git.publish",
"group": "branch@6"
"group": "branch@7"
}
],
"git.remotes": [
@@ -1654,19 +1692,10 @@
"default": true
},
"git.autofetch": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "string",
"enum": [
"all"
]
}
],
"type": ["boolean", "string"],
"enum": [true, false, "all"],
"scope": "resource",
"description": "%config.autofetch%",
"markdownDescription": "%config.autofetch%",
"default": false,
"tags": [
"usesOnlineServices"
@@ -1675,7 +1704,7 @@
"git.autofetchPeriod": {
"type": "number",
"scope": "resource",
"description": "%config.autofetchPeriod%",
"markdownDescription": "%config.autofetchPeriod%",
"default": 180
},
"git.branchValidationRegex": {
@@ -2140,6 +2169,15 @@
"highContrast": "#c74e39"
}
},
{
"id": "gitDecoration.renamedResourceForeground",
"description": "%colors.renamed%",
"defaults": {
"light": "#007100",
"dark": "#73C991",
"highContrast": "#73C991"
}
},
{
"id": "gitDecoration.untrackedResourceForeground",
"description": "%colors.untracked%",
@@ -2162,9 +2200,9 @@
"id": "gitDecoration.conflictingResourceForeground",
"description": "%colors.conflict%",
"defaults": {
"light": "#6c6cc4",
"dark": "#6c6cc4",
"highContrast": "#6c6cc4"
"light": "#ad0707",
"dark": "#e4676b",
"highContrast": "#c74e39"
}
},
{
@@ -2305,6 +2343,13 @@
"when": "config.git.enabled",
"enablement": "git.state == initialized",
"group": "5_scm@1"
},
{
"view": "explorer",
"contents": "%view.workbench.learnMore%",
"when": "config.git.enabled",
"enablement": "git.state == initialized",
"group": "5_scm@10"
}
]
},
@@ -2312,8 +2357,8 @@
"byline": "^5.0.0",
"file-type": "^7.2.0",
"iconv-lite-umd": "0.6.8",
"jschardet": "2.2.1",
"vscode-extension-telemetry": "0.1.1",
"jschardet": "2.3.0",
"vscode-extension-telemetry": "0.1.7",
"vscode-nls": "^4.0.0",
"vscode-uri": "^2.0.0",
"which": "^1.3.0"

View File

@@ -90,6 +90,8 @@
"command.timelineOpenDiff": "Open Changes",
"command.timelineCopyCommitId": "Copy Commit ID",
"command.timelineCopyCommitMessage": "Copy Commit Message",
"command.timelineSelectForCompare": "Select for Compare",
"command.timelineCompareWithSelected": "Compare with Selected",
"config.enabled": "Whether git is enabled.",
"config.path": "Path and filename of the git executable, e.g. `C:\\Program Files\\Git\\bin\\git.exe` (Windows). This can also be an array of string values containing multiple paths to look up.",
"config.autoRepositoryDetection": "Configures when repositories should be automatically detected.",
@@ -98,8 +100,8 @@
"config.autoRepositoryDetection.subFolders": "Scan for subfolders of the currently opened folder.",
"config.autoRepositoryDetection.openEditors": "Scan for parent folders of open files.",
"config.autorefresh": "Whether auto refreshing is enabled.",
"config.autofetch": "When set to true, commits will automatically be fetched from the default remote of the current Git repository. Setting to `all` will fetch from all remotes",
"config.autofetchPeriod": "Duration in seconds between each automatic git fetch, when `git.autofetch` is enabled.",
"config.autofetch": "When set to true, commits will automatically be fetched from the default remote of the current Git repository. Setting to `all` will fetch from all remotes.",
"config.autofetchPeriod": "Duration in seconds between each automatic git fetch, when `#git.autofetch#` is enabled.",
"config.confirmSync": "Confirm before synchronizing git repositories.",
"config.countBadge": "Controls the Git count badge.",
"config.countBadge.all": "Count all changes.",
@@ -124,7 +126,7 @@
"config.suggestSmartCommit": "Suggests to enable smart commit (commit all changes when there are no staged changes).",
"config.enableCommitSigning": "Enables commit signing with GPG or X.509.",
"config.discardAllScope": "Controls what changes are discarded by the `Discard all changes` command. `all` discards all changes. `tracked` discards only tracked files. `prompt` shows a prompt dialog every time the action is run.",
"config.decorations.enabled": "Controls whether Git contributes colors and badges to the explorer and the open editors view.",
"config.decorations.enabled": "Controls whether Git contributes colors and badges to the Explorer and the Open Editors view.",
"config.enableStatusBarSync": "Controls whether the Git Sync command appears in the status bar.",
"config.followTagsWhenSync": "Follow push all tags when running the sync command.",
"config.promptToSaveFilesBeforeStash": "Controls whether Git should check for unsaved files before stashing changes.",
@@ -175,11 +177,11 @@
"config.untrackedChanges.mixed": "All changes, tracked and untracked, appear together and behave equally.",
"config.untrackedChanges.separate": "Untracked changes appear separately in the Source Control view. They are also excluded from several actions.",
"config.untrackedChanges.hidden": "Untracked changes are hidden and excluded from several actions.",
"config.requireGitUserConfig": "Controls whether to require explicit Git user configuration or allow Git to guess if missing",
"config.requireGitUserConfig": "Controls whether to require explicit Git user configuration or allow Git to guess if missing.",
"config.showCommitInput": "Controls whether to show the commit input in the Git source control panel.",
"config.terminalAuthentication": "Controls whether to enable VS Code to be the authentication handler for git processes spawned in the integrated terminal. Note: terminals need to be restarted to pick up a change in this setting.",
"config.timeline.showAuthor": "Controls whether to show the commit author in the Timeline view",
"config.timeline.date": "Controls which date to use for items in the Timeline view",
"config.timeline.showAuthor": "Controls whether to show the commit author in the Timeline view.",
"config.timeline.date": "Controls which date to use for items in the Timeline view.",
"config.timeline.date.committed": "Use the committed date",
"config.timeline.date.authored": "Use the authored date",
"config.useCommitInputAsStashMessage": "Controls whether to use the message from the commit input box as the default stash message.",
@@ -196,6 +198,7 @@
"colors.added": "Color for added resources.",
"colors.modified": "Color for modified resources.",
"colors.deleted": "Color for deleted resources.",
"colors.renamed": "Color for renamed or copied resources.",
"colors.untracked": "Color for untracked resources.",
"colors.ignored": "Color for ignored resources.",
"colors.conflict": "Color for resources with conflicts.",
@@ -206,5 +209,6 @@
"view.workbench.scm.folder": "The folder currently open doesn't have a git repository. You can initialize a repository which will enable source control features powered by git.\n[Initialize Repository](command:git.init?%5Btrue%5D)\nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).",
"view.workbench.scm.workspace": "The workspace currently open doesn't have any folders containing git repositories. You can initialize a repository on a folder which will enable source control features powered by git.\n[Initialize Repository](command:git.init)\nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).",
"view.workbench.scm.emptyWorkspace": "The workspace currently open doesn't have any folders containing git repositories.\n[Add Folder to Workspace](command:workbench.action.addRootFolder)\nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).",
"view.workbench.cloneRepository": "You can also clone a repository from a URL. To learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).\n[Clone Repository](command:git.clone 'Clone a repository once the git extension has activated')"
"view.workbench.cloneRepository": "You can clone a repository locally.\n[Clone Repository](command:git.clone 'Clone a repository once the git extension has activated')",
"view.workbench.learnMore": "To learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm)."
}

View File

@@ -5,7 +5,7 @@
import { Model } from '../model';
import { Repository as BaseRepository, Resource } from '../repository';
import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, ForcePushMode, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, RefType, RemoteSourceProvider, CredentialsProvider, BranchQuery, PushErrorHandler, PublishEvent, ICloneOptions } from './git'; // {{SQL CARBON EDIT}} add ICloneOptions
import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, ForcePushMode, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, RefType, RemoteSourceProvider, CredentialsProvider, BranchQuery, PushErrorHandler, PublishEvent, FetchOptions, ICloneOptions } from './git'; // {{SQL CARBON EDIT}} add ICloneOptions
import { Event, SourceControlInputBox, Uri, SourceControl, Disposable, commands, CancellationToken } from 'vscode'; // {{SQL CARBON EDIT}} add CancellationToken
import { mapEvent } from '../util';
import { toGitUri } from '../uri';
@@ -193,8 +193,16 @@ export class ApiRepository implements Repository {
return this._repository.renameRemote(name, newName);
}
fetch(remote?: string | undefined, ref?: string | undefined, depth?: number | undefined): Promise<void> {
return this._repository.fetch(remote, ref, depth);
fetch(arg0?: FetchOptions | string | undefined,
ref?: string | undefined,
depth?: number | undefined,
prune?: boolean | undefined
): Promise<void> {
if (arg0 !== undefined && typeof arg0 !== 'string') {
return this._repository.fetch(arg0);
}
return this._repository.fetch({ remote: arg0, ref, depth, prune });
}
pull(unshallow?: boolean): Promise<void> {

View File

@@ -139,6 +139,14 @@ export interface CommitOptions {
requireUserConfig?: boolean;
}
export interface FetchOptions {
remote?: string;
ref?: string;
all?: boolean;
prune?: boolean;
depth?: number;
}
export interface BranchQuery {
readonly remote?: boolean;
readonly pattern?: string;
@@ -197,6 +205,7 @@ export interface Repository {
removeRemote(name: string): Promise<void>;
renameRemote(name: string, newName: string): Promise<void>;
fetch(options?: FetchOptions): Promise<void>;
fetch(remote?: string, ref?: string, depth?: number): Promise<void>;
pull(unshallow?: boolean): Promise<void>;
push(remoteName?: string, branchName?: string, setUpstream?: boolean, force?: ForcePushMode): Promise<void>;

View File

@@ -30,7 +30,7 @@ function main(argv: string[]): void {
const output = process.env['VSCODE_GIT_ASKPASS_PIPE'] as string;
const request = argv[2];
const host = argv[4].replace(/^["']+|["']+$/g, '');
const host = argv[4].replace(/^["']+|["':]+$/g, '');
const ipcClient = new IPCClient('askpass');
ipcClient.call({ request, host }).then(res => {

View File

@@ -5,7 +5,7 @@
import * as os from 'os';
import * as path from 'path';
import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider } from 'vscode';
import { Command, commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider } from 'vscode';
import TelemetryReporter from 'vscode-extension-telemetry';
import * as nls from 'vscode-nls';
import { Branch, ForcePushMode, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourceProvider } from './api/git';
@@ -43,18 +43,18 @@ class CheckoutItem implements QuickPickItem {
class CheckoutTagItem extends CheckoutItem {
get description(): string {
override get description(): string {
return localize('tag at', "Tag at {0}", this.shortCommit);
}
}
class CheckoutRemoteHeadItem extends CheckoutItem {
get description(): string {
override get description(): string {
return localize('remote branch at', "Remote branch at {0}", this.shortCommit);
}
async run(repository: Repository, opts?: { detached?: boolean }): Promise<void> {
override async run(repository: Repository, opts?: { detached?: boolean }): Promise<void> {
if (!this.ref.name) {
return;
}
@@ -153,21 +153,21 @@ class AddRemoteItem implements QuickPickItem {
}
}
interface CommandOptions {
interface ScmCommandOptions {
repository?: boolean;
diff?: boolean;
}
interface Command {
interface ScmCommand {
commandId: string;
key: string;
method: Function;
options: CommandOptions;
options: ScmCommandOptions;
}
const Commands: Command[] = [];
const Commands: ScmCommand[] = [];
function command(commandId: string, options: CommandOptions = {}): Function {
function command(commandId: string, options: ScmCommandOptions = {}): Function {
return (_target: any, key: string, descriptor: any) => {
if (!(typeof descriptor.value === 'function')) {
throw new Error('not supported');
@@ -797,7 +797,7 @@ export class CommandCenter {
return;
}
const from = path.relative(repository.root, fromUri.path);
const from = path.relative(repository.root, fromUri.fsPath);
let to = await window.showInputBox({
value: from,
valueSelection: [from.length - path.basename(from).length, from.length]
@@ -2247,8 +2247,8 @@ export class CommandCenter {
return;
}
await repository.addRemote(name, url);
await repository.fetch(name);
await repository.addRemote(name, url.trim());
await repository.fetch({ remote: name });
return name;
}
@@ -2613,6 +2613,22 @@ export class CommandCenter {
@command('git.timeline.openDiff', { repository: false })
async timelineOpenDiff(item: TimelineItem, uri: Uri | undefined, _source: string) {
const cmd = this.resolveTimelineOpenDiffCommand(
item, uri,
{
preserveFocus: true,
preview: true,
viewColumn: ViewColumn.Active
},
);
if (cmd === undefined) {
return undefined;
}
return commands.executeCommand(cmd.command, ...(cmd.arguments ?? []));
}
resolveTimelineOpenDiffCommand(item: TimelineItem, uri: Uri | undefined, options?: TextDocumentShowOptions): Command | undefined {
if (uri === undefined || uri === null || !GitTimelineItem.is(item)) {
return undefined;
}
@@ -2629,13 +2645,11 @@ export class CommandCenter {
title = localize('git.title.diffRefs', '{0} ({1}) ⟷ {0} ({2})', basename, item.shortPreviousRef, item.shortRef);
}
const options: TextDocumentShowOptions = {
preserveFocus: true,
preview: true,
viewColumn: ViewColumn.Active
return {
command: 'vscode.diff',
title: 'Open Comparison',
arguments: [toGitUri(uri, item.previousRef), item.ref === '' ? uri : toGitUri(uri, item.ref), title, options]
};
return commands.executeCommand('vscode.diff', toGitUri(uri, item.previousRef), item.ref === '' ? uri : toGitUri(uri, item.ref), title, options);
}
@command('git.timeline.copyCommitId', { repository: false })
@@ -2656,6 +2670,52 @@ export class CommandCenter {
env.clipboard.writeText(item.message);
}
private _selectedForCompare: { uri: Uri, item: GitTimelineItem } | undefined;
@command('git.timeline.selectForCompare', { repository: false })
async timelineSelectForCompare(item: TimelineItem, uri: Uri | undefined, _source: string) {
if (!GitTimelineItem.is(item) || !uri) {
return;
}
this._selectedForCompare = { uri, item };
await commands.executeCommand('setContext', 'git.timeline.selectedForCompare', true);
}
@command('git.timeline.compareWithSelected', { repository: false })
async timelineCompareWithSelected(item: TimelineItem, uri: Uri | undefined, _source: string) {
if (!GitTimelineItem.is(item) || !uri || !this._selectedForCompare || uri.toString() !== this._selectedForCompare.uri.toString()) {
return;
}
const { item: selected } = this._selectedForCompare;
const basename = path.basename(uri.fsPath);
let leftTitle;
if ((selected.previousRef === 'HEAD' || selected.previousRef === '~') && selected.ref === '') {
leftTitle = localize('git.title.workingTree', '{0} (Working Tree)', basename);
}
else if (selected.previousRef === 'HEAD' && selected.ref === '~') {
leftTitle = localize('git.title.index', '{0} (Index)', basename);
} else {
leftTitle = localize('git.title.ref', '{0} ({1})', basename, selected.shortRef);
}
let rightTitle;
if ((item.previousRef === 'HEAD' || item.previousRef === '~') && item.ref === '') {
rightTitle = localize('git.title.workingTree', '{0} (Working Tree)', basename);
}
else if (item.previousRef === 'HEAD' && item.ref === '~') {
rightTitle = localize('git.title.index', '{0} (Index)', basename);
} else {
rightTitle = localize('git.title.ref', '{0} ({1})', basename, item.shortRef);
}
const title = localize('git.title.diff', '{0} ⟷ {1}', leftTitle, rightTitle);
await commands.executeCommand('vscode.diff', selected.ref === '' ? uri : toGitUri(uri, selected.ref), item.ref === '' ? uri : toGitUri(uri, item.ref), title);
}
@command('git.rebaseAbort', { repository: true })
async rebaseAbort(repository: Repository): Promise<void> {
if (repository.rebaseCommit) {
@@ -2665,7 +2725,7 @@ export class CommandCenter {
}
}
private createCommand(id: string, key: string, method: Function, options: CommandOptions): (...args: any[]) => any {
private createCommand(id: string, key: string, method: Function, options: ScmCommandOptions): (...args: any[]) => any {
const result = (...args: any[]) => {
let result: Promise<any>;

View File

@@ -11,7 +11,7 @@ import * as which from 'which';
import { EventEmitter } from 'events';
import * as iconv from 'iconv-lite-umd';
import * as filetype from 'file-type';
import { assign, groupBy, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter } from './util';
import { assign, groupBy, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter, Versions } from './util';
import { CancellationToken, Uri } from 'vscode';
import { detectEncoding } from './encoding';
import { Ref, RefType, Branch, Remote, ForcePushMode, GitErrorCodes, LogOptions, Change, Status, CommitOptions, BranchQuery, ICloneOptions } from './api/git'; // {{SQL CARBON EDIT}} add ICloneOptions
@@ -377,6 +377,10 @@ export class Git {
this.env = options.env || {};
}
compareGitVersionTo(version: string): -1 | 0 | 1 {
return Versions.compare(Versions.fromString(this.version), Versions.fromString(version));
}
open(repository: string, dotGit: string): Repository {
return new Repository(this, repository, dotGit);
}
@@ -1638,7 +1642,7 @@ export class Repository {
err.gitErrorCode = GitErrorCodes.NoUserNameConfigured;
} else if (/Could not read from remote repository/.test(err.stderr || '')) {
err.gitErrorCode = GitErrorCodes.RemoteConnectionError;
} else if (/Pull is not possible because you have unmerged files|Cannot pull with rebase: You have unstaged changes|Your local changes to the following files would be overwritten|Please, commit your changes before you can merge/i.test(err.stderr)) {
} else if (/Pull(?:ing)? is not possible because you have unmerged files|Cannot pull with rebase: You have unstaged changes|Your local changes to the following files would be overwritten|Please, commit your changes before you can merge/i.test(err.stderr)) {
err.stderr = err.stderr.replace(/Cannot pull with rebase: You have unstaged changes/i, 'Cannot pull with rebase, you have unstaged changes');
err.gitErrorCode = GitErrorCodes.DirtyWorkTree;
} else if (/cannot lock ref|unable to update local ref/i.test(err.stderr || '')) {
@@ -1977,7 +1981,16 @@ export class Repository {
return this.getHEAD();
}
const args = ['for-each-ref', '--format=%(refname)%00%(upstream:short)%00%(upstream:track)%00%(objectname)'];
const args = ['for-each-ref'];
let supportsAheadBehind = true;
if (this._git.compareGitVersionTo('1.9.0') === -1) {
args.push('--format=%(refname)%00%(upstream:short)%00%(objectname)');
supportsAheadBehind = false;
} else {
args.push('--format=%(refname)%00%(upstream:short)%00%(objectname)%00%(upstream:track)');
}
if (/^refs\/(head|remotes)\//i.test(name)) {
args.push(name);
} else {
@@ -1986,7 +1999,7 @@ export class Repository {
const result = await this.exec(args);
const branches: Branch[] = result.stdout.trim().split('\n').map<Branch | undefined>(line => {
let [branchName, upstream, status, ref] = line.trim().split('\0');
let [branchName, upstream, ref, status] = line.trim().split('\0');
if (branchName.startsWith('refs/heads/')) {
branchName = branchName.substring(11);
@@ -2026,7 +2039,19 @@ export class Repository {
}).filter((b?: Branch): b is Branch => !!b);
if (branches.length) {
return branches[0];
const [branch] = branches;
if (!supportsAheadBehind && branch.upstream) {
try {
const result = await this.exec(['rev-list', '--left-right', '--count', `${branch.name}...${branch.upstream.remote}/${branch.upstream.name}`]);
const [ahead, behind] = result.stdout.trim().split('\t');
(branch as any).ahead = Number(ahead) || 0;
(branch as any).behind = Number(behind) || 0;
} catch { }
}
return branch;
}
return Promise.reject<Branch>(new Error('No such branch'));

View File

@@ -73,12 +73,13 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann
git.onOutput.addListener('log', onOutput);
disposables.push(toDisposable(() => git.onOutput.removeListener('log', onOutput)));
const cc = new CommandCenter(git, model, outputChannel, telemetryReporter);
disposables.push(
new CommandCenter(git, model, outputChannel, telemetryReporter),
cc,
new GitFileSystemProvider(model),
new GitDecorations(model),
new GitProtocolHandler(),
new GitTimelineProvider(model)
new GitTimelineProvider(model, cc)
);
// checkGitVersion(info); {{SQL CARBON EDIT}} Don't check git version

View File

@@ -284,8 +284,9 @@ export class Model implements IRemoteSourceProviderRegistry, IPushErrorHandlerRe
this.open(repository);
await repository.status();
} catch (err) {
} catch (ex) {
// noop
this.outputChannel.appendLine(`Opening repository for path='${path}' failed; ex=${ex}`);
}
}

View File

@@ -7,7 +7,7 @@ import * as fs from 'fs';
import * as path from 'path';
import { CancellationToken, Command, Disposable, Event, EventEmitter, Memento, OutputChannel, ProgressLocation, ProgressOptions, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, ThemeColor, Uri, window, workspace, WorkspaceEdit, FileDecoration, commands } from 'vscode';
import * as nls from 'vscode-nls';
import { Branch, Change, ForcePushMode, GitErrorCodes, LogOptions, Ref, RefType, Remote, Status, CommitOptions, BranchQuery } from './api/git';
import { Branch, Change, ForcePushMode, GitErrorCodes, LogOptions, Ref, RefType, Remote, Status, CommitOptions, BranchQuery, FetchOptions } from './api/git';
import { AutoFetcher } from './autofetch';
import { debounce, memoize, throttle } from './decorators';
import { Commit, GitError, Repository as BaseRepository, Stash, Submodule, LogFileOptions } from './git';
@@ -55,13 +55,13 @@ export class Resource implements SourceControlResourceState {
case Status.UNTRACKED: return localize('untracked', "Untracked");
case Status.IGNORED: return localize('ignored', "Ignored");
case Status.INTENT_TO_ADD: return localize('intent to add', "Intent to Add");
case Status.BOTH_DELETED: return localize('both deleted', "Both Deleted");
case Status.ADDED_BY_US: return localize('added by us', "Added By Us");
case Status.DELETED_BY_THEM: return localize('deleted by them', "Deleted By Them");
case Status.ADDED_BY_THEM: return localize('added by them', "Added By Them");
case Status.DELETED_BY_US: return localize('deleted by us', "Deleted By Us");
case Status.BOTH_ADDED: return localize('both added', "Both Added");
case Status.BOTH_MODIFIED: return localize('both modified', "Both Modified");
case Status.BOTH_DELETED: return localize('both deleted', "Conflict: Both Deleted");
case Status.ADDED_BY_US: return localize('added by us', "Conflict: Added By Us");
case Status.DELETED_BY_THEM: return localize('deleted by them', "Conflict: Deleted By Them");
case Status.ADDED_BY_THEM: return localize('added by them', "Conflict: Added By Them");
case Status.DELETED_BY_US: return localize('deleted by us', "Conflict: Deleted By Us");
case Status.BOTH_ADDED: return localize('both added', "Conflict: Both Added");
case Status.BOTH_MODIFIED: return localize('both modified', "Conflict: Both Modified");
default: return '';
}
}
@@ -199,12 +199,13 @@ export class Resource implements SourceControlResourceState {
case Status.DELETED_BY_US:
return 'D';
case Status.INDEX_COPIED:
return 'C';
case Status.BOTH_DELETED:
case Status.ADDED_BY_US:
case Status.ADDED_BY_THEM:
case Status.BOTH_ADDED:
case Status.BOTH_MODIFIED:
return 'C';
return '!'; // Using ! instead of ⚠, because the latter looks really bad on windows
default:
throw new Error('Unknown git status: ' + this.type);
}
@@ -223,12 +224,13 @@ export class Resource implements SourceControlResourceState {
case Status.INDEX_ADDED:
case Status.INTENT_TO_ADD:
return new ThemeColor('gitDecoration.addedResourceForeground');
case Status.INDEX_COPIED:
case Status.INDEX_RENAMED:
return new ThemeColor('gitDecoration.renamedResourceForeground');
case Status.UNTRACKED:
return new ThemeColor('gitDecoration.untrackedResourceForeground');
case Status.IGNORED:
return new ThemeColor('gitDecoration.ignoredResourceForeground');
case Status.INDEX_COPIED:
case Status.BOTH_DELETED:
case Status.ADDED_BY_US:
case Status.DELETED_BY_THEM:
@@ -246,10 +248,10 @@ export class Resource implements SourceControlResourceState {
switch (this.type) {
case Status.INDEX_MODIFIED:
case Status.MODIFIED:
case Status.INDEX_COPIED:
return 2;
case Status.IGNORED:
return 3;
case Status.INDEX_COPIED:
case Status.BOTH_DELETED:
case Status.ADDED_BY_US:
case Status.DELETED_BY_THEM:
@@ -1317,8 +1319,8 @@ export class Repository implements Disposable {
await this._fetch({ all: true });
}
async fetch(remote?: string, ref?: string, depth?: number): Promise<void> {
await this._fetch({ remote, ref, depth });
async fetch(options: FetchOptions): Promise<void> {
await this._fetch(options);
}
private async _fetch(options: { remote?: string, ref?: string, all?: boolean, prune?: boolean, depth?: number, silent?: boolean; } = {}): Promise<void> {
@@ -1833,7 +1835,10 @@ export class Repository implements Disposable {
// noop
}
const sort = config.get<'alphabetically' | 'committerdate'>('branchSortOrder') || 'alphabetically';
let sort = config.get<'alphabetically' | 'committerdate'>('branchSortOrder') || 'alphabetically';
if (sort !== 'alphabetically' && sort !== 'committerdate') {
sort = 'alphabetically';
}
const [refs, remotes, submodules, rebaseCommit] = await Promise.all([this.repository.getRefs({ sort }), this.repository.getRemotes(), this.repository.getSubmodules(), this.getRebaseCommit()]);
this._HEAD = HEAD;

View File

@@ -8,7 +8,7 @@ const testRunner = require('../../../../test/integration/electron/testrunner');
const options: any = {
ui: 'tdd',
useColors: (!process.env.BUILD_ARTIFACTSTAGINGDIRECTORY && process.platform !== 'win32'),
color: true,
timeout: 60000
};

View File

@@ -45,8 +45,10 @@ suite('git smoke test', function () {
cp.execSync('git init', { cwd });
cp.execSync('git config user.name testuser', { cwd });
cp.execSync('git config user.email monacotools@microsoft.com', { cwd });
cp.execSync('git config commit.gpgsign false', { cwd });
cp.execSync('git add .', { cwd });
cp.execSync('git commit -m "initial commit"', { cwd });
cp.execSync('git branch -m main', { cwd });
// make sure git is activated
const ext = extensions.getExtension<GitExtension>('vscode.git');
@@ -124,7 +126,7 @@ suite('git smoke test', function () {
assert.equal(repository.state.workingTreeChanges.length, 0);
assert.equal(repository.state.indexChanges.length, 0);
});
test('rename/delete conflict', async function () {
cp.execSync('git branch test', { cwd });
cp.execSync('git checkout test', { cwd });
@@ -133,16 +135,16 @@ suite('git smoke test', function () {
cp.execSync('git add .', { cwd });
await repository.commit('commit on test');
cp.execSync('git checkout master', { cwd });
cp.execSync('git checkout main', { cwd });
fs.renameSync(file('app.js'), file('rename.js'));
cp.execSync('git add .', { cwd });
await repository.commit('commit on master');
await repository.commit('commit on main');
try {
cp.execSync('git merge test', { cwd });
} catch (e) { }
setTimeout(() => {
commands.executeCommand('workbench.scm.focus');
}, 2e3);

View File

@@ -9,6 +9,7 @@ import { Model } from './model';
import { Repository, Resource } from './repository';
import { debounce } from './decorators';
import { emojify, ensureEmojis } from './emoji';
import { CommandCenter } from './commands';
const localize = nls.loadMessageBundle();
@@ -73,7 +74,7 @@ export class GitTimelineProvider implements TimelineProvider {
private repoDisposable: Disposable | undefined;
private repoStatusDate: Date | undefined;
constructor(private readonly model: Model) {
constructor(private readonly model: Model, private commands: CommandCenter) {
this.disposable = Disposable.from(
model.onDidOpenRepository(this.onRepositoriesChanged, this),
workspace.onDidChangeConfiguration(this.onConfigurationChanged, this)
@@ -161,16 +162,20 @@ export class GitTimelineProvider implements TimelineProvider {
const message = emojify(c.message);
const item = new GitTimelineItem(c.hash, commits[i + 1]?.hash ?? `${c.hash}^`, message, date?.getTime() ?? 0, c.hash, 'git:file:commit');
item.iconPath = new (ThemeIcon as any)('git-commit');
item.iconPath = new ThemeIcon('git-commit');
if (showAuthor) {
item.description = c.authorName;
}
item.detail = `${c.authorName} (${c.authorEmail}) — ${c.hash.substr(0, 8)}\n${dateFormatter.format(date)}\n\n${message}`;
item.command = {
title: 'Open Comparison',
command: 'git.timeline.openDiff',
arguments: [item, uri, this.id]
};
const cmd = this.commands.resolveTimelineOpenDiffCommand(item, uri);
if (cmd) {
item.command = {
title: 'Open Comparison',
command: cmd.command,
arguments: cmd.arguments,
};
}
return item;
});
@@ -184,14 +189,18 @@ export class GitTimelineProvider implements TimelineProvider {
const item = new GitTimelineItem('~', 'HEAD', localize('git.timeline.stagedChanges', 'Staged Changes'), date.getTime(), 'index', 'git:file:index');
// TODO@eamodio: Replace with a better icon -- reflecting its status maybe?
item.iconPath = new (ThemeIcon as any)('git-commit');
item.iconPath = new ThemeIcon('git-commit');
item.description = '';
item.detail = localize('git.timeline.detail', '{0} — {1}\n{2}\n\n{3}', you, localize('git.index', 'Index'), dateFormatter.format(date), Resource.getStatusText(index.type));
item.command = {
title: 'Open Comparison',
command: 'git.timeline.openDiff',
arguments: [item, uri, this.id]
};
const cmd = this.commands.resolveTimelineOpenDiffCommand(item, uri);
if (cmd) {
item.command = {
title: 'Open Comparison',
command: cmd.command,
arguments: cmd.arguments,
};
}
items.splice(0, 0, item);
}
@@ -202,14 +211,18 @@ export class GitTimelineProvider implements TimelineProvider {
const item = new GitTimelineItem('', index ? '~' : 'HEAD', localize('git.timeline.uncommitedChanges', 'Uncommitted Changes'), date.getTime(), 'working', 'git:file:working');
// TODO@eamodio: Replace with a better icon -- reflecting its status maybe?
item.iconPath = new (ThemeIcon as any)('git-commit');
item.iconPath = new ThemeIcon('git-commit');
item.description = '';
item.detail = localize('git.timeline.detail', '{0} — {1}\n{2}\n\n{3}', you, localize('git.workingTree', 'Working Tree'), dateFormatter.format(date), Resource.getStatusText(working.type));
item.command = {
title: 'Open Comparison',
command: 'git.timeline.openDiff',
arguments: [item, uri, this.id]
};
const cmd = this.commands.resolveTimelineOpenDiffCommand(item, uri);
if (cmd) {
item.command = {
title: 'Open Comparison',
command: cmd.command,
arguments: cmd.arguments,
};
}
items.splice(0, 0, item);
}

View File

@@ -414,3 +414,56 @@ export class PromiseSource<T> {
}
}
}
export namespace Versions {
declare type VersionComparisonResult = -1 | 0 | 1;
export interface Version {
major: number;
minor: number;
patch: number;
pre?: string;
}
export function compare(v1: string | Version, v2: string | Version): VersionComparisonResult {
if (typeof v1 === 'string') {
v1 = fromString(v1);
}
if (typeof v2 === 'string') {
v2 = fromString(v2);
}
if (v1.major > v2.major) { return 1; }
if (v1.major < v2.major) { return -1; }
if (v1.minor > v2.minor) { return 1; }
if (v1.minor < v2.minor) { return -1; }
if (v1.patch > v2.patch) { return 1; }
if (v1.patch < v2.patch) { return -1; }
if (v1.pre === undefined && v2.pre !== undefined) { return 1; }
if (v1.pre !== undefined && v2.pre === undefined) { return -1; }
if (v1.pre !== undefined && v2.pre !== undefined) {
return v1.pre.localeCompare(v2.pre) as VersionComparisonResult;
}
return 0;
}
export function from(major: string | number, minor: string | number, patch?: string | number, pre?: string): Version {
return {
major: typeof major === 'string' ? parseInt(major, 10) : major,
minor: typeof minor === 'string' ? parseInt(minor, 10) : minor,
patch: patch === undefined || patch === null ? 0 : typeof patch === 'string' ? parseInt(patch, 10) : patch,
pre: pre,
};
}
export function fromString(version: string): Version {
const [ver, pre] = version.split('-');
const [major, minor, patch] = ver.split('.');
return from(major, minor, patch, pre);
}
}

View File

@@ -1,5 +1,5 @@
{
"extends": "../shared.tsconfig.json",
"extends": "../tsconfig.base.json",
"compilerOptions": {
"outDir": "./out",
"experimentalDecorators": true,
@@ -10,4 +10,4 @@
"include": [
"src/**/*"
]
}
}

View File

@@ -41,14 +41,30 @@ ansi-regex@^3.0.0:
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
applicationinsights@1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5"
integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg==
applicationinsights@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.7.4.tgz#e7d96435594d893b00cf49f70a5927105dbb8749"
integrity sha512-XFLsNlcanpjFhHNvVWEfcm6hr7lu9znnb6Le1Lk5RE03YUV9X2B2n2MfM4kJZRrUdV+C0hdHxvWyv+vWoLfY7A==
dependencies:
cls-hooked "^4.2.2"
continuation-local-storage "^3.2.1"
diagnostic-channel "0.2.0"
diagnostic-channel-publishers "0.2.1"
zone.js "0.7.6"
diagnostic-channel-publishers "^0.3.3"
async-hook-jl@^1.7.6:
version "1.7.6"
resolved "https://registry.yarnpkg.com/async-hook-jl/-/async-hook-jl-1.7.6.tgz#4fd25c2f864dbaf279c610d73bf97b1b28595e68"
integrity sha512-gFaHkFfSxTjvoxDMYqDuGHlcRyUuamF8s+ZTtJdDzqjws4mCt7v0vuV79/E2Wr2/riMQgtG4/yUtXWs1gZ7JMg==
dependencies:
stack-chain "^1.3.7"
async-listener@^0.6.0:
version "0.6.10"
resolved "https://registry.yarnpkg.com/async-listener/-/async-listener-0.6.10.tgz#a7c97abe570ba602d782273c0de60a51e3e17cbc"
integrity sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw==
dependencies:
semver "^5.3.0"
shimmer "^1.1.0"
balanced-match@^1.0.0:
version "1.0.0"
@@ -78,6 +94,15 @@ charenc@~0.0.1:
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=
cls-hooked@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/cls-hooked/-/cls-hooked-4.2.2.tgz#ad2e9a4092680cdaffeb2d3551da0e225eae1908"
integrity sha512-J4Xj5f5wq/4jAvcdgoGsL3G103BtWpZrMo8NEinRltN+xpTZdI+M38pyQqhuFU/P792xkMFvnKSf+Lm81U1bxw==
dependencies:
async-hook-jl "^1.7.6"
emitter-listener "^1.0.1"
semver "^5.4.1"
commander@2.9.0:
version "2.9.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4"
@@ -90,6 +115,14 @@ concat-map@0.0.1:
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
continuation-local-storage@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz#11f613f74e914fe9b34c92ad2d28fe6ae1db7ffb"
integrity sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA==
dependencies:
async-listener "^0.6.0"
emitter-listener "^1.1.1"
crypt@~0.0.1:
version "0.0.2"
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
@@ -110,16 +143,16 @@ debug@^2.2.0:
ms "2.0.0"
debug@^3.1.0:
version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
version "3.2.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
dependencies:
ms "^2.1.1"
diagnostic-channel-publishers@0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3"
integrity sha1-ji1geottef6IC1SLxYzGvrKIxPM=
diagnostic-channel-publishers@^0.3.3:
version "0.3.5"
resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.3.5.tgz#a84a05fd6cc1d7619fdd17791c17e540119a7536"
integrity sha512-AOIjw4T7Nxl0G2BoBPhkQ6i7T4bUd9+xvdYizwvG7vVAM1dvr+SDrcUudlmzwH0kbEwdR2V1EcnKT0wAeYLQNQ==
diagnostic-channel@0.2.0:
version "0.2.0"
@@ -133,6 +166,13 @@ diff@3.2.0:
resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9"
integrity sha1-yc45Okt8vQsFinJck98pkCeGj/k=
emitter-listener@^1.0.1, emitter-listener@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/emitter-listener/-/emitter-listener-1.1.2.tgz#56b140e8f6992375b3d7cb2cab1cc7432d9632e8"
integrity sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==
dependencies:
shimmer "^1.2.0"
escape-string-regexp@1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
@@ -208,10 +248,10 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
jschardet@2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.2.1.tgz#03b0264669a90c7a5c436a68c5a7d4e4cb0c9823"
integrity sha512-Ks2JNuUJoc7PGaZ7bVFtSEvOcr0rBq6Q1J5/7+zKWLT+g+4zziL63O0jg7y2jxhzIa1LVsHUbPXrbaWmz9iwDw==
jschardet@2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.3.0.tgz#06e2636e16c8ada36feebbdc08aa34e6a9b3ff75"
integrity sha512-6I6xT7XN/7sBB7q8ObzKbmv5vN+blzLcboDE1BNEsEfmRXJValMxO6OIRT69ylPBRemS3rw6US+CMCar0OBc9g==
json3@3.3.2:
version "3.3.2"
@@ -371,6 +411,21 @@ semver@^5.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==
semver@^5.4.1:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
shimmer@^1.1.0, shimmer@^1.2.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337"
integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==
stack-chain@^1.3.7:
version "1.3.7"
resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285"
integrity sha1-0ZLJ/06moiyUxN1FkXHj8AzqEoU=
strip-ansi@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
@@ -385,12 +440,12 @@ supports-color@3.1.2:
dependencies:
has-flag "^1.0.0"
vscode-extension-telemetry@0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b"
integrity sha512-TkKKG/B/J94DP5qf6xWB4YaqlhWDg6zbbqVx7Bz//stLQNnfE9XS1xm3f6fl24c5+bnEK0/wHgMgZYKIKxPeUA==
vscode-extension-telemetry@0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.7.tgz#18389bc24127c89dade29cd2b71ba69a6ee6ad26"
integrity sha512-pZuZTHO9OpsrwlerOKotWBRLRYJ53DobYb7aWiRAXjlqkuqE+YJJaP+2WEy8GrLIF1EnitXTDMaTAKsmLQ5ORQ==
dependencies:
applicationinsights "1.0.8"
applicationinsights "1.7.4"
vscode-nls@^4.0.0:
version "4.0.0"
@@ -418,8 +473,3 @@ xml@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5"
integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=
zone.js@0.7.6:
version "0.7.6"
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009"
integrity sha1-+7w50+AmHQmG8boGMG6zrrDSIAk=

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -8,6 +8,7 @@
"engines": {
"vscode": "^1.41.0"
},
"icon": "images/icon.png",
"enableProposedApi": true,
"categories": [
"Other"
@@ -20,6 +21,12 @@
"activationEvents": [
"onAuthenticationRequest:github"
],
"capabilities": {
"virtualWorkspaces": true,
"untrustedWorkspaces": {
"supported": true
}
},
"contributes": {
"commands": [
{
@@ -53,10 +60,11 @@
"vscode:prepublish": "npm run compile"
},
"dependencies": {
"node-fetch": "2.6.0",
"node-fetch": "2.6.1",
"uuid": "8.1.0",
"vscode-extension-telemetry": "0.1.1",
"vscode-nls": "^4.1.2"
"vscode-extension-telemetry": "0.1.7",
"vscode-nls": "^4.1.2",
"vscode-tas-client": "^0.1.22"
},
"devDependencies": {
"@types/node": "^12.19.9",

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event, Disposable } from 'vscode';
import { EventEmitter, Event, Disposable } from 'vscode';
export function filterEvent<T>(event: Event<T>, filter: (e: T) => boolean): Event<T> {
return (listener, thisArgs = null, disposables?) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables);
@@ -47,27 +47,54 @@ const passthrough = (value: any, resolve: (value?: any) => void) => resolve(valu
* @param adapter controls resolution of the returned promise
* @returns a promise that resolves or rejects as specified by the adapter
*/
export async function promiseFromEvent<T, U>(
export function promiseFromEvent<T, U>(
event: Event<T>,
adapter: PromiseAdapter<T, U> = passthrough): Promise<U> {
adapter: PromiseAdapter<T, U> = passthrough): { promise: Promise<U>, cancel: EventEmitter<void> } {
let subscription: Disposable;
return new Promise<U>((resolve, reject) =>
subscription = event((value: T) => {
try {
Promise.resolve(adapter(value, resolve, reject))
.catch(reject);
} catch (error) {
reject(error);
let cancel = new EventEmitter<void>();
return {
promise: new Promise<U>((resolve, reject) => {
cancel.event(_ => reject());
subscription = event((value: T) => {
try {
Promise.resolve(adapter(value, resolve, reject))
.catch(reject);
} catch (error) {
reject(error);
}
});
}).then(
(result: U) => {
subscription.dispose();
return result;
},
error => {
subscription.dispose();
throw error;
}
})
).then(
(result: U) => {
subscription.dispose();
return result;
},
error => {
subscription.dispose();
throw error;
}
);
),
cancel
};
}
export function arrayEquals<T>(one: ReadonlyArray<T> | undefined, other: ReadonlyArray<T> | undefined, itemEquals: (a: T, b: T) => boolean = (a, b) => a === b): boolean {
if (one === other) {
return true;
}
if (!one || !other) {
return false;
}
if (one.length !== other.length) {
return false;
}
for (let i = 0, len = one.length; i < len; i++) {
if (!itemEquals(one[i], other[i])) {
return false;
}
}
return true;
}

View File

@@ -0,0 +1,73 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import TelemetryReporter from 'vscode-extension-telemetry';
import { getExperimentationService, IExperimentationService, IExperimentationTelemetry, TargetPopulation } from 'vscode-tas-client';
export class ExperimentationTelemetry implements IExperimentationTelemetry {
private sharedProperties: Record<string, string> = {};
constructor(private baseReporter: TelemetryReporter) { }
sendTelemetryEvent(eventName: string, properties?: Record<string, string>, measurements?: Record<string, number>) {
this.baseReporter.sendTelemetryEvent(
eventName,
{
...this.sharedProperties,
...properties,
},
measurements,
);
}
sendTelemetryErrorEvent(
eventName: string,
properties?: Record<string, string>,
_measurements?: Record<string, number>,
) {
this.baseReporter.sendTelemetryErrorEvent(eventName, {
...this.sharedProperties,
...properties,
});
}
setSharedProperty(name: string, value: string): void {
this.sharedProperties[name] = value;
}
postEvent(eventName: string, props: Map<string, string>): void {
const event: Record<string, string> = {};
for (const [key, value] of props) {
event[key] = value;
}
this.sendTelemetryEvent(eventName, event);
}
dispose(): Promise<any> {
return this.baseReporter.dispose();
}
}
function getTargetPopulation(): TargetPopulation {
switch (vscode.env.uriScheme) {
case 'vscode':
return TargetPopulation.Public;
case 'vscode-insiders':
return TargetPopulation.Insiders;
case 'vscode-exploration':
return TargetPopulation.Internal;
case 'code-oss':
return TargetPopulation.Team;
default:
return TargetPopulation.Public;
}
}
export async function createExperimentationService(context: vscode.ExtensionContext, telemetry: ExperimentationTelemetry): Promise<IExperimentationService> {
const id = context.extension.id;
const version = context.extension.packageJSON.version;
return getExperimentationService(id, version, getTargetPopulation(), telemetry, context.globalState);
}

View File

@@ -8,13 +8,17 @@ import { GitHubAuthenticationProvider, onDidChangeSessions } from './github';
import { uriHandler } from './githubServer';
import Logger from './common/logger';
import TelemetryReporter from 'vscode-extension-telemetry';
import { createExperimentationService, ExperimentationTelemetry } from './experimentationService';
export async function activate(context: vscode.ExtensionContext) {
const { name, version, aiKey } = require('../package.json') as { name: string, version: string, aiKey: string };
const telemetryReporter = new TelemetryReporter(name, version, aiKey);
const telemetryReporter = new ExperimentationTelemetry(new TelemetryReporter(name, version, aiKey));
const experimentationService = await createExperimentationService(context, telemetryReporter);
await experimentationService.initialFetch;
context.subscriptions.push(vscode.window.registerUriHandler(uriHandler));
const loginService = new GitHubAuthenticationProvider(context);
const loginService = new GitHubAuthenticationProvider(context, telemetryReporter);
await loginService.initialize(context);
@@ -24,17 +28,17 @@ export async function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.authentication.registerAuthenticationProvider('github', 'GitHub', {
onDidChangeSessions: onDidChangeSessions.event,
getSessions: () => Promise.resolve(loginService.sessions),
login: async (scopeList: string[]) => {
getSessions: (scopes?: string[]) => loginService.getSessions(scopes),
createSession: async (scopeList: string[]) => {
try {
/* __GDPR__
"login" : { }
*/
telemetryReporter.sendTelemetryEvent('login');
const session = await loginService.login(scopeList.sort().join(' '));
const session = await loginService.createSession(scopeList.sort().join(' '));
Logger.info('Login success!');
onDidChangeSessions.fire({ added: [session.id], removed: [], changed: [] });
onDidChangeSessions.fire({ added: [session], removed: [], changed: [] });
return session;
} catch (e) {
// If login was cancelled, do not notify user.
@@ -56,15 +60,17 @@ export async function activate(context: vscode.ExtensionContext) {
throw e;
}
},
logout: async (id: string) => {
removeSession: async (id: string) => {
try {
/* __GDPR__
"logout" : { }
*/
telemetryReporter.sendTelemetryEvent('logout');
await loginService.logout(id);
onDidChangeSessions.fire({ added: [], removed: [id], changed: [] });
const session = await loginService.removeSession(id);
if (session) {
onDidChangeSessions.fire({ added: [], removed: [session], changed: [] });
}
} catch (e) {
/* __GDPR__
"logoutFailed" : { }

View File

@@ -8,6 +8,8 @@ import { v4 as uuid } from 'uuid';
import { Keychain } from './common/keychain';
import { GitHubServer, NETWORK_ERROR } from './githubServer';
import Logger from './common/logger';
import { arrayEquals } from './common/utils';
import { ExperimentationTelemetry } from './experimentationService';
export const onDidChangeSessions = new vscode.EventEmitter<vscode.AuthenticationProviderAuthenticationSessionsChangeEvent>();
@@ -24,12 +26,13 @@ interface SessionData {
export class GitHubAuthenticationProvider {
private _sessions: vscode.AuthenticationSession[] = [];
private _githubServer = new GitHubServer();
private _githubServer: GitHubServer;
private _keychain: Keychain;
constructor(context: vscode.ExtensionContext) {
constructor(context: vscode.ExtensionContext, telemetryReporter: ExperimentationTelemetry) {
this._keychain = new Keychain(context);
this._githubServer = new GitHubServer(telemetryReporter);
}
public async initialize(context: vscode.ExtensionContext): Promise<void> {
@@ -43,11 +46,18 @@ export class GitHubAuthenticationProvider {
context.subscriptions.push(context.secrets.onDidChange(() => this.checkForUpdates()));
}
async getSessions(scopes?: string[]): Promise<vscode.AuthenticationSession[]> {
return scopes
? this._sessions.filter(session => arrayEquals(session.scopes, scopes))
: this._sessions;
}
private async verifySessions(): Promise<void> {
const verifiedSessions: vscode.AuthenticationSession[] = [];
const verificationPromises = this._sessions.map(async session => {
try {
await this._githubServer.getUserInfo(session.accessToken);
this._githubServer.checkIsEdu(session.accessToken);
verifiedSessions.push(session);
} catch (e) {
// Remove sessions that return unauthorized response
@@ -74,8 +84,8 @@ export class GitHubAuthenticationProvider {
return;
}
const added: string[] = [];
const removed: string[] = [];
const added: vscode.AuthenticationSession[] = [];
const removed: vscode.AuthenticationSession[] = [];
storedSessions.forEach(session => {
const matchesExisting = this._sessions.some(s => s.id === session.id);
@@ -83,7 +93,7 @@ export class GitHubAuthenticationProvider {
if (!matchesExisting) {
Logger.info('Adding session found in keychain');
this._sessions.push(session);
added.push(session.id);
added.push(session);
}
});
@@ -97,7 +107,7 @@ export class GitHubAuthenticationProvider {
this._sessions.splice(sessionIndex, 1);
}
removed.push(session.id);
removed.push(session);
}
});
@@ -153,9 +163,10 @@ export class GitHubAuthenticationProvider {
return this._sessions;
}
public async login(scopes: string): Promise<vscode.AuthenticationSession> {
public async createSession(scopes: string): Promise<vscode.AuthenticationSession> {
const token = await this._githubServer.login(scopes);
const session = await this.tokenToSession(token, scopes.split(' '));
this._githubServer.checkIsEdu(token);
await this.setToken(session);
return session;
}
@@ -185,15 +196,18 @@ export class GitHubAuthenticationProvider {
await this.storeSessions();
}
public async logout(id: string) {
public async removeSession(id: string): Promise<vscode.AuthenticationSession | undefined> {
Logger.info(`Logging out of ${id}`);
const sessionIndex = this._sessions.findIndex(session => session.id === id);
let session: vscode.AuthenticationSession | undefined;
if (sessionIndex > -1) {
session = this._sessions[sessionIndex];
this._sessions.splice(sessionIndex, 1);
} else {
Logger.error('Session not found');
}
await this.storeSessions();
return session;
}
}

View File

@@ -9,11 +9,13 @@ import fetch, { Response } from 'node-fetch';
import { v4 as uuid } from 'uuid';
import { PromiseAdapter, promiseFromEvent } from './common/utils';
import Logger from './common/logger';
import { ExperimentationTelemetry } from './experimentationService';
const localize = nls.loadMessageBundle();
export const NETWORK_ERROR = 'network error';
const AUTH_RELAY_SERVER = 'vscode-auth.github.com';
// const AUTH_RELAY_STAGING_SERVER = 'client-auth-staging-14a768b.herokuapp.com';
class UriEventHandler extends vscode.EventEmitter<vscode.Uri> implements vscode.UriHandler {
public handleUri(uri: vscode.Uri) {
@@ -39,10 +41,18 @@ export class GitHubServer {
private _statusBarItem: vscode.StatusBarItem | undefined;
private _pendingStates = new Map<string, string[]>();
private _codeExchangePromises = new Map<string, Promise<string>>();
private _codeExchangePromises = new Map<string, { promise: Promise<string>, cancel: vscode.EventEmitter<void> }>();
constructor(private readonly telemetryReporter: ExperimentationTelemetry) { }
private isTestEnvironment(url: vscode.Uri): boolean {
return url.authority === 'vscode-web-test-playground.azurewebsites.net' || url.authority.startsWith('localhost:');
return /\.azurewebsites\.net$/.test(url.authority) || url.authority.startsWith('localhost:');
}
// TODO@joaomoreno TODO@RMacfarlane
private async isNoCorsEnvironment(): Promise<boolean> {
const uri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/did-authenticate`));
return uri.scheme === 'https' && /^vscode\./.test(uri.authority);
}
public async login(scopes: string): Promise<string> {
@@ -50,7 +60,10 @@ export class GitHubServer {
this.updateStatusBarItem(true);
const state = uuid();
const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/did-authenticate`));
// TODO@joaomoreno TODO@RMacfarlane
const nocors = await this.isNoCorsEnvironment();
const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/did-authenticate${nocors ? '?nocors=true' : ''}`));
if (this.isTestEnvironment(callbackUri)) {
const token = await vscode.window.showInputBox({ prompt: 'GitHub Personal Access Token', ignoreFocusOut: true });
@@ -77,23 +90,30 @@ export class GitHubServer {
const existingStates = this._pendingStates.get(scopes) || [];
this._pendingStates.set(scopes, [...existingStates, state]);
const uri = vscode.Uri.parse(`https://${AUTH_RELAY_SERVER}/authorize/?callbackUri=${encodeURIComponent(callbackUri.toString())}&scope=${scopes}&state=${state}&responseType=code&authServer=https://github.com`);
const uri = vscode.Uri.parse(`https://${AUTH_RELAY_SERVER}/authorize/?callbackUri=${encodeURIComponent(callbackUri.toString())}&scope=${scopes}&state=${state}&responseType=code&authServer=https://github.com${nocors ? '&nocors=true' : ''}`);
await vscode.env.openExternal(uri);
}
// Register a single listener for the URI callback, in case the user starts the login process multiple times
// before completing it.
let existingPromise = this._codeExchangePromises.get(scopes);
if (!existingPromise) {
existingPromise = promiseFromEvent(uriHandler.event, this.exchangeCodeForToken(scopes));
this._codeExchangePromises.set(scopes, existingPromise);
let codeExchangePromise = this._codeExchangePromises.get(scopes);
if (!codeExchangePromise) {
codeExchangePromise = promiseFromEvent(uriHandler.event, this.exchangeCodeForToken(scopes));
this._codeExchangePromises.set(scopes, codeExchangePromise);
}
return Promise.race([
existingPromise,
promiseFromEvent<string | undefined, string>(onDidManuallyProvideToken.event, (token: string | undefined): string => { if (!token) { throw new Error('Cancelled'); } return token; })
codeExchangePromise.promise,
promiseFromEvent<string | undefined, string>(onDidManuallyProvideToken.event, (token: string | undefined, resolve, reject): void => {
if (!token) {
reject('Cancelled');
} else {
resolve(token);
}
}).promise
]).finally(() => {
this._pendingStates.delete(scopes);
codeExchangePromise?.cancel.fire();
this._codeExchangePromises.delete(scopes);
this.updateStatusBarItem(false);
});
@@ -111,23 +131,36 @@ export class GitHubServer {
return;
}
try {
const result = await fetch(`https://${AUTH_RELAY_SERVER}/token?code=${code}&state=${query.state}`, {
method: 'POST',
headers: {
Accept: 'application/json'
}
});
const url = `https://${AUTH_RELAY_SERVER}/token?code=${code}&state=${query.state}`;
if (result.ok) {
const json = await result.json();
// TODO@joao: remove
if (query.nocors) {
try {
const json: any = await vscode.commands.executeCommand('_workbench.fetchJSON', url, 'POST');
Logger.info('Token exchange success!');
resolve(json.access_token);
} else {
reject(result.statusText);
} catch (err) {
reject(err);
}
} else {
try {
const result = await fetch(url, {
method: 'POST',
headers: {
Accept: 'application/json'
}
});
if (result.ok) {
const json = await result.json();
Logger.info('Token exchange success!');
resolve(json.access_token);
} else {
reject(result.statusText);
}
} catch (ex) {
reject(ex);
}
} catch (ex) {
reject(ex);
}
};
@@ -153,7 +186,7 @@ export class GitHubServer {
}
try {
const uri = vscode.Uri.parse(uriOrToken);
const uri = vscode.Uri.parse(uriOrToken.trim());
if (!uri.scheme || uri.scheme === 'file') { throw new Error; }
uriHandler.handleUri(uri);
} catch (e) {
@@ -210,4 +243,42 @@ export class GitHubServer {
throw new Error(result.statusText);
}
}
public async checkIsEdu(token: string): Promise<void> {
const nocors = await this.isNoCorsEnvironment();
if (nocors) {
return;
}
try {
const result = await fetch('https://education.github.com/api/user', {
headers: {
Authorization: `token ${token}`,
'faculty-check-preview': 'true',
'User-Agent': 'Visual-Studio-Code'
}
});
if (result.ok) {
const json: { student: boolean, faculty: boolean } = await result.json();
/* __GDPR__
"session" : {
"isEdu": { "classification": "NonIdentifiableDemographicInfo", "purpose": "FeatureInsight" }
}
*/
this.telemetryReporter.sendTelemetryEvent('session', {
isEdu: json.student
? 'student'
: json.faculty
? 'faculty'
: 'none'
});
}
} catch (e) {
// No-op
}
}
}

View File

@@ -1,5 +1,5 @@
{
"extends": "../shared.tsconfig.json",
"extends": "../tsconfig.base.json",
"compilerOptions": {
"outDir": "./out",
"experimentalDecorators": true,

View File

@@ -25,20 +25,52 @@
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.0.0.tgz#165aae4819ad2174a17476dbe66feebd549556c0"
integrity sha512-xSQfNcvOiE5f9dyd4Kzxbof1aTrLobL278pGLKOZI6esGfZ7ts9Ka16CzIN6Y8hFHE1C7jIBZokULhK1bOgjRw==
applicationinsights@1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5"
integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg==
applicationinsights@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.7.4.tgz#e7d96435594d893b00cf49f70a5927105dbb8749"
integrity sha512-XFLsNlcanpjFhHNvVWEfcm6hr7lu9znnb6Le1Lk5RE03YUV9X2B2n2MfM4kJZRrUdV+C0hdHxvWyv+vWoLfY7A==
dependencies:
cls-hooked "^4.2.2"
continuation-local-storage "^3.2.1"
diagnostic-channel "0.2.0"
diagnostic-channel-publishers "0.2.1"
zone.js "0.7.6"
diagnostic-channel-publishers "^0.3.3"
async-hook-jl@^1.7.6:
version "1.7.6"
resolved "https://registry.yarnpkg.com/async-hook-jl/-/async-hook-jl-1.7.6.tgz#4fd25c2f864dbaf279c610d73bf97b1b28595e68"
integrity sha512-gFaHkFfSxTjvoxDMYqDuGHlcRyUuamF8s+ZTtJdDzqjws4mCt7v0vuV79/E2Wr2/riMQgtG4/yUtXWs1gZ7JMg==
dependencies:
stack-chain "^1.3.7"
async-listener@^0.6.0:
version "0.6.10"
resolved "https://registry.yarnpkg.com/async-listener/-/async-listener-0.6.10.tgz#a7c97abe570ba602d782273c0de60a51e3e17cbc"
integrity sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw==
dependencies:
semver "^5.3.0"
shimmer "^1.1.0"
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
axios@^0.21.1:
version "0.21.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
dependencies:
follow-redirects "^1.10.0"
cls-hooked@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/cls-hooked/-/cls-hooked-4.2.2.tgz#ad2e9a4092680cdaffeb2d3551da0e225eae1908"
integrity sha512-J4Xj5f5wq/4jAvcdgoGsL3G103BtWpZrMo8NEinRltN+xpTZdI+M38pyQqhuFU/P792xkMFvnKSf+Lm81U1bxw==
dependencies:
async-hook-jl "^1.7.6"
emitter-listener "^1.0.1"
semver "^5.4.1"
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
@@ -46,15 +78,23 @@ combined-stream@^1.0.8:
dependencies:
delayed-stream "~1.0.0"
continuation-local-storage@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz#11f613f74e914fe9b34c92ad2d28fe6ae1db7ffb"
integrity sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA==
dependencies:
async-listener "^0.6.0"
emitter-listener "^1.1.1"
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
diagnostic-channel-publishers@0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3"
integrity sha1-ji1geottef6IC1SLxYzGvrKIxPM=
diagnostic-channel-publishers@^0.3.3:
version "0.3.5"
resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.3.5.tgz#a84a05fd6cc1d7619fdd17791c17e540119a7536"
integrity sha512-AOIjw4T7Nxl0G2BoBPhkQ6i7T4bUd9+xvdYizwvG7vVAM1dvr+SDrcUudlmzwH0kbEwdR2V1EcnKT0wAeYLQNQ==
diagnostic-channel@0.2.0:
version "0.2.0"
@@ -63,6 +103,18 @@ diagnostic-channel@0.2.0:
dependencies:
semver "^5.3.0"
emitter-listener@^1.0.1, emitter-listener@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/emitter-listener/-/emitter-listener-1.1.2.tgz#56b140e8f6992375b3d7cb2cab1cc7432d9632e8"
integrity sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==
dependencies:
shimmer "^1.2.0"
follow-redirects@^1.10.0:
version "1.13.3"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267"
integrity sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA==
form-data@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682"
@@ -84,34 +136,53 @@ mime-types@^2.1.12:
dependencies:
mime-db "1.44.0"
node-fetch@2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
node-fetch@2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
semver@^5.3.0:
semver@^5.3.0, semver@^5.4.1:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
shimmer@^1.1.0, shimmer@^1.2.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337"
integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==
stack-chain@^1.3.7:
version "1.3.7"
resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285"
integrity sha1-0ZLJ/06moiyUxN1FkXHj8AzqEoU=
tas-client@0.1.21:
version "0.1.21"
resolved "https://registry.yarnpkg.com/tas-client/-/tas-client-0.1.21.tgz#62275d5f75266eaae408f7463364748cb92f220d"
integrity sha512-7UuIwOXarCYoCTrQHY5n7M+63XuwMC0sVUdbPQzxqDB9wMjIW0JF39dnp3yoJnxr4jJUVhPtvkkXZbAD0BxCcA==
dependencies:
axios "^0.21.1"
uuid@8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.1.0.tgz#6f1536eb43249f473abc6bd58ff983da1ca30d8d"
integrity sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg==
vscode-extension-telemetry@0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b"
integrity sha512-TkKKG/B/J94DP5qf6xWB4YaqlhWDg6zbbqVx7Bz//stLQNnfE9XS1xm3f6fl24c5+bnEK0/wHgMgZYKIKxPeUA==
vscode-extension-telemetry@0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.7.tgz#18389bc24127c89dade29cd2b71ba69a6ee6ad26"
integrity sha512-pZuZTHO9OpsrwlerOKotWBRLRYJ53DobYb7aWiRAXjlqkuqE+YJJaP+2WEy8GrLIF1EnitXTDMaTAKsmLQ5ORQ==
dependencies:
applicationinsights "1.0.8"
applicationinsights "1.7.4"
vscode-nls@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167"
integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==
zone.js@0.7.6:
version "0.7.6"
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009"
integrity sha1-+7w50+AmHQmG8boGMG6zrrDSIAk=
vscode-tas-client@^0.1.22:
version "0.1.22"
resolved "https://registry.yarnpkg.com/vscode-tas-client/-/vscode-tas-client-0.1.22.tgz#2dd674b21a94ff4e97db2b6545d9efda8b5f07c3"
integrity sha512-1sYH73nhiSRVQgfZkLQNJW7VzhKM9qNbCe8QyXgiKkLhH4GflDXRPAK4yy4P41jUgula+Fc9G7i5imj1dlKfaw==
dependencies:
tas-client "0.1.21"

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -8,6 +8,7 @@
"engines": {
"vscode": "^1.41.0"
},
"icon": "images/icon.png",
"enableProposedApi": true,
"categories": [
"Other"
@@ -19,6 +20,12 @@
"vscode.git"
],
"main": "./out/extension.js",
"capabilities": {
"virtualWorkspaces": false,
"untrustedWorkspaces": {
"supported": true
}
},
"contributes": {
"commands": [
{

View File

@@ -168,7 +168,12 @@ export async function publishRepository(gitAPI: GitAPI, repository?: Repository)
}
const githubRepository = await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, cancellable: false, title: 'Publish to GitHub' }, async progress => {
progress.report({ message: `Publishing to GitHub ${isPrivate ? 'private' : 'public'} repository`, increment: 25 });
progress.report({
message: isPrivate
? localize('publishing_private', "Publishing to a private GitHub repository")
: localize('publishing_public', "Publishing to a public GitHub repository"),
increment: 25
});
const res = await octokit.repos.createForAuthenticatedUser({
name: repo!,
@@ -177,7 +182,7 @@ export async function publishRepository(gitAPI: GitAPI, repository?: Repository)
const createdGithubRepository = res.data;
progress.report({ message: 'Creating first commit', increment: 25 });
progress.report({ message: localize('publishing_firstcommit', "Creating first commit"), increment: 25 });
if (!repository) {
repository = await gitAPI.init(folder) || undefined;
@@ -189,9 +194,11 @@ export async function publishRepository(gitAPI: GitAPI, repository?: Repository)
await repository.commit('first commit', { all: true });
}
progress.report({ message: 'Uploading files', increment: 25 });
progress.report({ message: localize('publishing_uploading', "Uploading files"), increment: 25 });
const branch = await repository.getBranch('HEAD');
await repository.addRemote('origin', createdGithubRepository.clone_url);
await repository.push('origin', 'master', true);
await repository.push('origin', branch.name, true);
return createdGithubRepository;
});
@@ -200,9 +207,9 @@ export async function publishRepository(gitAPI: GitAPI, repository?: Repository)
return;
}
const openInGitHub = 'Open In GitHub';
vscode.window.showInformationMessage(`Successfully published the '${owner}/${repo}' repository on GitHub.`, openInGitHub).then(action => {
if (action === openInGitHub) {
const openOnGitHub = localize('openingithub', "Open on GitHub");
vscode.window.showInformationMessage(localize('publishing_done', "Successfully published the '{0}' repository to GitHub.", `${owner}/${repo}`), openOnGitHub).then(action => {
if (action === openOnGitHub) {
vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(githubRepository.html_url));
}
});

View File

@@ -3,37 +3,69 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { PushErrorHandler, GitErrorCodes, Repository, Remote } from './typings/git';
import { window, ProgressLocation, commands, Uri } from 'vscode';
import { commands, env, ProgressLocation, Uri, window } from 'vscode';
import * as nls from 'vscode-nls';
import { getOctokit } from './auth';
import { GitErrorCodes, PushErrorHandler, Remote, Repository } from './typings/git';
const localize = nls.loadMessageBundle();
type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T;
export function isInCodespaces(): boolean {
return env.remoteName === 'codespaces';
}
async function handlePushError(repository: Repository, remote: Remote, refspec: string, owner: string, repo: string): Promise<void> {
const yes = localize('create a fork', "Create Fork");
const no = localize('no', "No");
const answer = await window.showInformationMessage(localize('fork', "You don't have permissions to push to '{0}/{1}' on GitHub. Would you like to create a fork and push to it instead?", owner, repo), yes, no);
if (answer === no) {
return;
}
const match = /^([^:]*):([^:]*)$/.exec(refspec);
const localName = match ? match[1] : refspec;
const remoteName = match ? match[2] : refspec;
let remoteName = match ? match[2] : refspec;
const [octokit, ghRepository] = await window.withProgress({ location: ProgressLocation.Notification, cancellable: false, title: localize('create fork', 'Create GitHub fork') }, async progress => {
progress.report({ message: localize('forking', "Forking '{0}/{1}'...", owner, repo), increment: 33 });
const octokit = await getOctokit();
// Issue: what if the repo already exists?
const res = await octokit.repos.createFork({ owner, repo });
const ghRepository = res.data;
type CreateForkResponseData = Awaited<ReturnType<typeof octokit.repos.createFork>>['data'];
progress.report({ message: localize('pushing', "Pushing changes..."), increment: 33 });
// Issue: what if the repo already exists?
let ghRepository: CreateForkResponseData;
try {
if (isInCodespaces()) {
// Call into the codespaces extension to fork the repository
const resp = await commands.executeCommand<{ repository: CreateForkResponseData, ref: string }>('github.codespaces.forkRepository');
if (!resp) {
throw new Error('Unable to fork respository');
}
ghRepository = resp.repository;
if (resp.ref) {
let ref = resp.ref;
if (ref.startsWith('refs/heads/')) {
ref = ref.substr(11);
}
remoteName = ref;
}
} else {
const resp = await octokit.repos.createFork({ owner, repo });
ghRepository = resp.data;
}
} catch (ex) {
console.error(ex);
throw ex;
}
progress.report({ message: localize('forking_pushing', "Pushing changes..."), increment: 33 });
// Issue: what if there's already an `upstream` repo?
await repository.renameRemote(remote.name, 'upstream');
@@ -55,11 +87,11 @@ async function handlePushError(repository: Repository, remote: Remote, refspec:
// yield
(async () => {
const openInGitHub = localize('openingithub', "Open In GitHub");
const openOnGitHub = localize('openingithub', "Open on GitHub");
const createPR = localize('createpr', "Create PR");
const action = await window.showInformationMessage(localize('done', "The fork '{0}' was successfully created on GitHub.", ghRepository.full_name), openInGitHub, createPR);
const action = await window.showInformationMessage(localize('forking_done', "The fork '{0}' was successfully created on GitHub.", ghRepository.full_name), openOnGitHub, createPR);
if (action === openInGitHub) {
if (action === openOnGitHub) {
await commands.executeCommand('vscode.open', Uri.parse(ghRepository.html_url));
} else if (action === createPR) {
const pr = await window.withProgress({ location: ProgressLocation.Notification, cancellable: false, title: localize('createghpr', "Creating GitHub Pull Request...") }, async _ => {
@@ -103,13 +135,12 @@ export class GithubPushErrorHandler implements PushErrorHandler {
return false;
}
if (!remote.pushUrl) {
const remoteUrl = remote.pushUrl || (isInCodespaces() ? remote.fetchUrl : undefined);
if (!remoteUrl) {
return false;
}
const match = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\.git/i.exec(remote.pushUrl)
|| /^git@github\.com:([^/]+)\/([^/]+)\.git/i.exec(remote.pushUrl);
const match = /^(?:https:\/\/github\.com\/|git@github\.com:)([^/]+)\/([^/.]+)(?:\.git)?$/i.exec(remoteUrl);
if (!match) {
return false;
}

View File

@@ -45,8 +45,8 @@ export class GithubRemoteSourceProvider implements RemoteSourceProvider {
}
const all = await Promise.all([
this.getQueryRemoteSources(octokit, query),
this.getUserRemoteSources(octokit, query),
this.getQueryRemoteSources(octokit, query)
]);
const map = new Map<string, RemoteSource>();
@@ -76,7 +76,7 @@ export class GithubRemoteSourceProvider implements RemoteSourceProvider {
return [];
}
const raw = await octokit.search.repos({ q: query, sort: 'updated' });
const raw = await octokit.search.repos({ q: query, sort: 'stars' });
return raw.data.items.map(asRemoteSource);
}

View File

@@ -1,5 +1,5 @@
{
"extends": "../shared.tsconfig.json",
"extends": "../tsconfig.base.json",
"compilerOptions": {
"outDir": "./out",
"experimentalDecorators": true,

View File

@@ -125,9 +125,9 @@ is-plain-object@^3.0.0:
integrity sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==
node-fetch@^2.3.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
once@^1.4.0:
version "1.4.0"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -26,6 +26,12 @@
"onCommand:imagePreview.zoomIn",
"onCommand:imagePreview.zoomOut"
],
"capabilities": {
"virtualWorkspaces": true,
"untrustedWorkspaces": {
"supported": true
}
},
"contributes": {
"customEditors": [
{
@@ -55,12 +61,12 @@
"commandPalette": [
{
"command": "imagePreview.zoomIn",
"when": "imagePreviewFocus",
"when": "activeCustomEditorId == 'imagePreview.previewEditor'",
"group": "1_imagePreview"
},
{
"command": "imagePreview.zoomOut",
"when": "imagePreviewFocus",
"when": "activeCustomEditorId == 'imagePreview.previewEditor'",
"group": "1_imagePreview"
}
]
@@ -75,7 +81,7 @@
"watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose"
},
"dependencies": {
"vscode-extension-telemetry": "0.1.1",
"vscode-extension-telemetry": "0.1.7",
"vscode-nls": "^4.0.0"
},
"repository": {

View File

@@ -11,7 +11,7 @@ export abstract class PreviewStatusBarEntry extends Disposable {
protected readonly entry: vscode.StatusBarItem;
constructor(options: vscode.window.StatusBarItemOptions) {
constructor(options: vscode.StatusBarItemOptions) {
super();
this.entry = this._register(vscode.window.createStatusBarItem(options));
}

Some files were not shown because too many files have changed in this diff Show More