SQL Operations Studio Public Preview 1 (0.23) release source code

This commit is contained in:
Karl Burtram
2017-11-09 14:30:27 -08:00
parent b88ecb8d93
commit 3cdac41339
8829 changed files with 759707 additions and 286 deletions

View File

View File

@@ -0,0 +1,34 @@
{
"name": "account-provider-azure",
"version": "0.0.1",
"publisher": "Microsoft",
"engines": { "vscode": "*" },
"main": "./out/main",
"activationEvents": [ "*" ],
"scripts": {
"compile": "gulp compile-extension:account-provider-azure"
},
"dependencies": {
"adal-node": "^0.1.16",
"decompress": "^4.0.0",
"extensions-modules": "file:../../extensions-modules",
"fs-extra-promise": "^0.3.1",
"opener": "1.4.2",
"request": "2.63.0",
"urijs": "^1.18.12",
"vscode-extension-telemetry": "^0.0.5",
"vscode-nls": "2.0.2"
},
"devDependencies": {
"@types/node": "^8.0.24"
},
"contributes": {
"commands": [
{
"command": "extension.clearTokenCache",
"title": "%extension.clearTokenCache%",
"category": "Azure Accounts"
}
]
}
}

View File

@@ -0,0 +1,3 @@
{
"extension.clearTokenCache": "Clear Azure Account Token Cache"
}

View File

@@ -0,0 +1,371 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as fs from 'fs';
import * as path from 'path';
import * as adal from 'adal-node';
import * as data from 'data';
import * as request from 'request';
import * as nls from 'vscode-nls';
import {
Arguments,
AzureAccount,
AzureAccountProviderMetadata,
AzureAccountSecurityTokenCollection,
Tenant
} from './interfaces';
const localize = nls.loadMessageBundle();
export class AzureAccountProvider implements data.AccountProvider {
private static WorkSchoolAccountLogo: data.AccountContextualLogo = {
light: AzureAccountProvider.loadIcon('work_school_account.svg'),
dark: AzureAccountProvider.loadIcon('work_school_account_inverse.svg')
};
private static MicrosoftAccountLogo: data.AccountContextualLogo = {
light: AzureAccountProvider.loadIcon('microsoft_account.svg'),
dark: AzureAccountProvider.loadIcon('microsoft_account.svg')
};
private _args: Arguments;
private _isInitialized: boolean;
private _tokenCache: adal.TokenCache;
public metadata: AzureAccountProviderMetadata;
constructor(metadata: AzureAccountProviderMetadata, tokenCache: adal.TokenCache) {
this.metadata = metadata;
this._args = {
host: metadata.settings.host,
clientId: metadata.settings.clientId
};
this._isInitialized = false;
this._tokenCache = tokenCache;
}
// PUBLIC METHODS //////////////////////////////////////////////////////
public clear(accountKey: data.AccountKey): Thenable<void> {
throw new Error('Not implemented');
}
public clearTokenCache(): Thenable<void> {
return this._tokenCache.clear();
}
public getSecurityToken(account: AzureAccount): Thenable<AzureAccountSecurityTokenCollection> {
let self = this;
return this.doIfInitialized(() => self.getAccessTokens(account));
}
public initialize(restoredAccounts: data.Account[]): Thenable<data.Account[]> {
let self = this;
return new Promise<data.Account[]>(resolve => {
// Rehydrate the accounts
restoredAccounts.forEach((account) => {
// Set the icon for the account
account.displayInfo.contextualLogo = account.properties.isMsAccount
? AzureAccountProvider.MicrosoftAccountLogo
: AzureAccountProvider.WorkSchoolAccountLogo;
// TODO: Set stale status based on whether we can authenticate or not
});
self._isInitialized = true;
resolve(restoredAccounts);
});
}
public prompt(): Thenable<AzureAccount> {
let self = this;
return this.doIfInitialized(() => self.signIn());
}
public refresh(account: AzureAccount): Thenable<AzureAccount> {
let self = this;
return this.doIfInitialized(() => self.signIn(account.properties.isMsAccount, account.key.accountId));
}
// PRIVATE METHODS /////////////////////////////////////////////////////
private static loadIcon(iconName: string) {
let filePath = path.join(__dirname, 'media', iconName);
try {
return 'image/svg+xml,' + fs.readFileSync(filePath);
} catch(e) {
return '';
}
}
private authenticate(tenantId: string, msa: boolean, userId: string, silent: boolean): Thenable<adal.TokenResponse> {
let authorityUrl = `${this.metadata.settings.host}/${tenantId}`;
// TODO: Rewrite using urijs
let authorizeUrl = `${authorityUrl}/oauth2/authorize`
+ `?client_id=${this.metadata.settings.clientId}` // Specify the client ID
+ `&response_type=code` // Request the authorization code grant flow
+ (userId ? (msa ? '&domain_hint=live.com' : '&msafed=0') : '') // Optimize prompt given existing MSA or org ID
+ ((!userId && !silent) ? '&prompt=login' : '') // Require login if not silent
+ (userId ? `&login_hint=${encodeURIComponent(userId)}` : '') // Show login hint if we have an existing user ID
+ (this.metadata.settings.siteId ? `&site_id=${this.metadata.settings.siteId}` : '') // Site ID to use as brand on the prompt
+ '&display=popup' // Causes a popup version of the UI to be shown
+ `&resource=${encodeURIComponent(this.metadata.settings.signInResourceId)}` // Specify the resource for which an access token should be retrieved
+ `&redirect_uri=${encodeURIComponent(this.metadata.settings.redirectUri)}`; // TODO: add locale parameter like in VSAccountProvider.TryAppendLocalParameter
// Get the authorization code. If this is the initial authentication (the user is unknown),
// do not silently prompt. If this is a subsequent authentication for an additional tenant,
// the browser cookie cache will be used to authenticate without prompting, so run the
// browser silently.
return data.accounts.performOAuthAuthorization(authorizeUrl, silent)
.then((code: string) => {
return new Promise((resolve, reject) => {
let context = new adal.AuthenticationContext(authorityUrl, null, this._tokenCache);
context.acquireTokenWithAuthorizationCode(
code,
this.metadata.settings.redirectUri,
this.metadata.settings.signInResourceId,
this.metadata.settings.clientId,
null,
(error, response) => {
if (error) {
reject(error);
} else {
resolve(<adal.TokenResponse> response);
}
}
);
});
});
}
private doIfInitialized<T>(op: () => Thenable<T>): Thenable<T> {
return this._isInitialized
? op()
: Promise.reject(localize('accountProviderNotInitialized', 'Account provider not initialized, cannot perform action'));
}
private getAccessTokens(account: AzureAccount): Thenable<AzureAccountSecurityTokenCollection> {
let self = this;
// TODO: Could we add some better typing here?
let accessTokenPromises: Thenable<void>[] = [];
let tokenCollection: AzureAccountSecurityTokenCollection = {};
for (let tenant of account.properties.tenants) {
let promise = new Promise<void>((resolve, reject) => {
// TODO: use urijs to generate this URI
let authorityUrl = `${self.metadata.settings.host}/${tenant.id}`;
let context = new adal.AuthenticationContext(authorityUrl, null, self._tokenCache);
// TODO: This is where we should mark the account as stale
context.acquireToken(
self.metadata.settings.armResource.id,
tenant.userId,
self.metadata.settings.clientId,
(error: adal.ErrorResponse, response: adal.SuccessResponse) => {
// Handle errors first
if (error) {
reject(error);
return;
}
// Generate a token object and add it to the collection
tokenCollection[tenant.id] = {
expiresOn: response.expiresOn,
resource: response.resource,
token: response.accessToken,
tokenType: response.tokenType
};
resolve();
}
);
});
accessTokenPromises.push(promise);
}
// Wait until all the tokens have been acquired then return the collection
return Promise.all(accessTokenPromises)
.then(() => tokenCollection);
}
private getTenantDisplayName(msa: boolean, tenantId: string, userId: string): Thenable<string> {
let self = this;
if(msa) {
return Promise.resolve(localize('microsoftAccountDisplayName', 'Microsoft Account'));
}
return new Promise<string>((resolve, reject) => {
// Get an access token to the AAD graph resource
// TODO: Use urijs to generate this URI
let authorityUrl = `${self.metadata.settings.host}/${tenantId}`;
let context = new adal.AuthenticationContext(authorityUrl, null, self._tokenCache);
context.acquireToken(self.metadata.settings.graphResource.id, userId, self.metadata.settings.clientId, (error, response) => {
if (error) {
reject(error);
return;
}
request.get(
`${self.metadata.settings.graphResource.endpoint}/${tenantId}/tenantDetails?api-version=2013-04-05`,
{
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${(<adal.TokenResponse>response).accessToken}`
},
json: true
},
(graphError, graphResponse, body: {error: any; value: any[];}) => {
if (graphError || body['odata.error']) {
reject(graphError || body['odata.error']);
} else if (body.value.length && body.value[0].displayName) {
resolve(body.value[0].displayName);
} else {
resolve(localize('azureWorkAccountDisplayName', 'Work or school account'));
}
}
);
});
});
}
private getTenants(msa: boolean, userId: string, tenantIds: string[], homeTenant: string): Thenable<Tenant[]> {
let self = this;
// Lookup each tenant ID that was provided
let getTenantPromises: Thenable<Tenant>[] = [];
for (let tenantId of tenantIds) {
let promise = this.authenticate(tenantId, msa, userId, false)
.then((response) => self.getTenantDisplayName(msa, response.tenantId, response.userId))
.then((displayName) => {
return <Tenant>{
displayName: displayName,
id: tenantId,
userId: userId
};
});
getTenantPromises.push(promise);
}
return Promise.all(getTenantPromises)
.then((tenants) => {
// Resort the tenants to make sure that the 'home' tenant is the first in the list
let homeTenantIndex = tenants.findIndex((tenant) => tenant.id === homeTenant);
if (homeTenantIndex >= 0) {
let homeTenant = tenants.splice(homeTenantIndex, 1);
tenants.unshift(homeTenant[0]);
}
return tenants;
});
}
private getTenantIds(userId: string): Thenable<string[]> {
return new Promise((resolve, reject) => {
// Get an access token to the ARM resource
// TODO: Build this URL with urijs
let authorityUrl = `${this.metadata.settings.host}/common`;
let context = new adal.AuthenticationContext(authorityUrl, null, this._tokenCache);
context.acquireToken(this.metadata.settings.armResource.id, userId, this.metadata.settings.clientId, (error, response) => {
if (error) {
reject(error);
return;
}
if (!!this.metadata.settings.adTenants && this.metadata.settings.adTenants.length > 0) {
resolve(this.metadata.settings.adTenants);
} else {
request.get(
`${this.metadata.settings.armResource.endpoint}/tenants?api-version=2015-01-01`,
{
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${(<adal.TokenResponse>response).accessToken}`
},
json: true
},
(armError, armResponse, body: {error: any; value: any[];}) => {
if (armError || body.error) {
reject(armError || body.error);
} else {
resolve(body.value.map(item => <string>item.tenantId));
}
}
);
}
});
});
}
private signIn(msa?: boolean, userId?: string): Thenable<AzureAccount> {
let self = this;
// Initial authentication is via the common/discovery tenant
return this.authenticate('common', msa, userId, false)
.then((response: adal.TokenResponse) => {
let identityProvider = response.identityProvider;
if (identityProvider) {
identityProvider = identityProvider.toLowerCase();
}
// Determine if this is a microsoft account
msa = identityProvider && (
identityProvider.indexOf('live.com') !== -1 ||
identityProvider.indexOf('live-int.com') !== -1 ||
identityProvider.indexOf('f8cdef31-a31e-4b4a-93e4-5f571e91255a') !== -1 ||
identityProvider.indexOf('ea8a4392-515e-481f-879e-6571ff2a8a36') !== -1);
// Get the user information
userId = response.userId;
let displayName = (response.givenName && response.familyName)
? `${response.givenName} ${response.familyName}`
: userId;
// Get all the additional tenants
return this.getTenantIds(userId)
.then(tenantIds => self.getTenants(msa, userId, tenantIds, response.tenantId))
.then(tenants => {
return <AzureAccount>{
key: {
providerId: self.metadata.id,
accountId: userId
},
name: userId,
displayInfo: {
contextualLogo: msa
? AzureAccountProvider.MicrosoftAccountLogo
: AzureAccountProvider.WorkSchoolAccountLogo,
contextualDisplayName: tenants[0].displayName,
displayName: displayName
},
properties: {
isMsAccount: msa,
tenants: tenants
},
isStale: false
};
});
});
}
}
// ADAL MONKEY PATCH ///////////////////////////////////////////////////////
// Monkey patch the ADAL TokenRequest class to fix the fact that when you request a token from an
// authorization code, it doesn't update the cache
import * as TokenRequest from 'adal-node/lib/token-request';
let getTokenWithAuthorizationCodeOriginal = TokenRequest.prototype.getTokenWithAuthorizationCode;
TokenRequest.prototype.getTokenWithAuthorizationCode = function (
authorizationCode: string,
clientSecret: string,
callback: adal.AcquireTokenCallback
) {
this._cacheDriver = this._createCacheDriver();
getTokenWithAuthorizationCodeOriginal.call(this, authorizationCode, clientSecret, (error: Error, response: adal.ErrorResponse|adal.TokenResponse) => {
if (error) {
callback(error, response);
} else {
this._userId = (<adal.TokenResponse> response).userId;
this._cacheDriver.add(response, () => callback(null, response));
}
});
};

View File

@@ -0,0 +1,87 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as constants from '../constants';
import * as data from 'data';
import * as events from 'events';
import * as nls from 'vscode-nls';
import * as path from 'path';
import * as vscode from 'vscode';
import CredentialServiceTokenCache from './tokenCache';
import providerSettings from './providerSettings';
import { AzureAccountProvider } from './azureAccountProvider';
import { AzureAccountProviderMetadata } from './interfaces';
let localize = nls.loadMessageBundle();
export class AzureAccountProviderService implements vscode.Disposable {
// CONSTANTS ///////////////////////////////////////////////////////////////
private static CommandClearTokenCache = 'extension.clearTokenCache';
private static CredentialNamespace = 'azureAccountProviderCredentials';
// MEMBER VARIABLES ////////////////////////////////////////////////////////
private _accountProviders: { [accountProviderId: string]: AzureAccountProvider };
private _event: events.EventEmitter;
constructor(private _context: vscode.ExtensionContext, private _userStoragePath: string) {
this._accountProviders = {};
this._event = new events.EventEmitter();
}
// PUBLIC METHODS //////////////////////////////////////////////////////
public activate(): Thenable<boolean> {
let self = this;
// Register commands
this._context.subscriptions.push(vscode.commands.registerCommand(
AzureAccountProviderService.CommandClearTokenCache,
() => { self._event.emit(AzureAccountProviderService.CommandClearTokenCache); }
));
this._event.on(AzureAccountProviderService.CommandClearTokenCache, () => { self.onClearTokenCache(); });
// Create the token caches
// 1) Get a credential provider
// 2) Iterate over the enabled providers
// 2a) Create a token cache for provider
// 2b) Create the provider from the provider's settings
// 2c) Register the provider with the account service
return data.credentials.getProvider(AzureAccountProviderService.CredentialNamespace)
.then(credProvider => {
providerSettings.forEach(provider => {
let tokenCacheKey = `azureTokenCache-${provider.metadata.id}`;
let tokenCachePath = path.join(self._userStoragePath, tokenCacheKey);
let tokenCache = new CredentialServiceTokenCache(credProvider, tokenCacheKey, tokenCachePath);
let accountProvider = new AzureAccountProvider(<AzureAccountProviderMetadata>provider.metadata, tokenCache);
self._accountProviders[provider.metadata.id] = accountProvider;
data.accounts.registerAccountProvider(provider.metadata, accountProvider);
});
})
.then(() => { return true; });
}
public dispose() { }
// PRIVATE HELPERS /////////////////////////////////////////////////////
private onClearTokenCache(): Thenable<void> {
let self = this;
let promises: Thenable<void>[] = providerSettings.map(provider => {
return self._accountProviders[provider.metadata.id].clearTokenCache();
});
return Promise.all(promises)
.then(
() => {
let message = localize('clearTokenCacheSuccess', 'Token cache successfully cleared');
vscode.window.showInformationMessage(`${constants.extensionName}: ${message}`);
},
err => {
let message = localize('clearTokenCacheFailure', 'Failed to clear token cache');
vscode.window.showErrorMessage(`${constants.extensionName}: ${message}: ${err}`);
});
}
}

View File

@@ -0,0 +1,167 @@
'use strict';
import * as data from 'data';
/**
* Represents a tenant (an Azure Active Directory instance) to which a user has access
*/
export interface Tenant {
/**
* Globally unique identifier of the tenant
*/
id: string;
/**
* Display name of the tenant
*/
displayName: string;
/**
* Identifier of the user in the tenant
*/
userId: string;
}
/**
* Represents a resource exposed by an Azure Active Directory
*/
export interface Resource {
/**
* Identifier of the resource
*/
id: string;
/**
* Endpoint url used to access the resource
*/
endpoint: string;
}
/**
* Represents the arguments that identify an instantiation of the AAD account provider
*/
export interface Arguments {
/**
* Host of the authority
*/
host: string;
/**
* Identifier of the client application
*/
clientId: string;
}
/**
* Represents settings for an AAD account provider
*/
export interface Settings {
/**
* Host of the authority
*/
host?: string;
/**
* Identifier of the client application
*/
clientId?: string;
/**
* Identifier of the resource to request when signing in
*/
signInResourceId?: string;
/**
* Information that describes the AAD graph resource
*/
graphResource?: Resource;
/**
* Information that describes the Azure resource management resource
*/
armResource?: Resource;
/**
* A list of tenant IDs to authenticate against. If defined, then these IDs will be used
* instead of querying the tenants endpoint of the armResource
*/
adTenants?: string[];
// AuthorizationCodeGrantFlowSettings //////////////////////////////////
/**
* An optional site ID that brands the interactive aspect of sign in
*/
siteId?: string;
/**
* Redirect URI that is used to signify the end of the interactive aspect of sign it
*/
redirectUri?: string;
}
/**
*
*/
export interface AzureAccountProviderMetadata extends data.AccountProviderMetadata {
/**
* Azure specific account provider settings.
*/
settings: Settings;
}
/**
* Properties specific to an Azure account
*/
export interface AzureAccountProperties {
/**
* Whether or not the account is a Microsoft account
*/
isMsAccount: boolean;
/**
* A list of tenants (aka directories) that the account belongs to
*/
tenants: Tenant[];
}
/**
* Override of the Account type to enforce properties that are AzureAccountProperties
*/
export interface AzureAccount extends data.Account {
/**
* AzureAccountProperties specifically used for Azure accounts
*/
properties: AzureAccountProperties;
}
/**
* Token returned from a request for an access token
*/
export interface AzureAccountSecurityToken {
/**
* Access token, itself
*/
token: string;
/**
* Date that the token expires on
*/
expiresOn: Date;
/**
* Name of the resource the token is good for (ie, management.core.windows.net)
*/
resource: string;
/**
* Type of the token (pretty much always 'Bearer')
*/
tokenType: string;
}
/**
* Azure account security token maps a tenant ID to the information returned from a request to get
* an access token. The list of tenants correspond to the tenants in the account properties.
*/
export type AzureAccountSecurityTokenCollection = {[tenantId: string]: AzureAccountSecurityToken};

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#f25022;}.cls-2{fill:#7fba00;}.cls-3{fill:#00a4ef;}.cls-4{fill:#ffb900;}</style></defs><title>microsoft_account_16x16</title><rect class="cls-1" width="7.6" height="7.6"/><rect class="cls-2" x="8.4" width="7.6" height="7.6"/><rect class="cls-3" y="8.4" width="7.6" height="7.6"/><rect class="cls-4" x="8.4" y="8.4" width="7.6" height="7.6"/></svg>

After

Width:  |  Height:  |  Size: 465 B

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>other_account_16x16</title><path d="M8,.28A7.71,7.71,0,1,0,15.72,8,7.73,7.73,0,0,0,8,.28ZM4.2,13.53a4,4,0,0,1,.13-.93,3.69,3.69,0,0,1,1-1.66A3.56,3.56,0,0,1,6,10.36a4,4,0,0,1,.9-.38,4.17,4.17,0,0,1,1-.13,3.79,3.79,0,0,1,1.48.29,3.61,3.61,0,0,1,2,2,3.74,3.74,0,0,1,.29,1.47A6.62,6.62,0,0,1,8,14.71,6.71,6.71,0,0,1,4.2,13.53Zm2-5a2.76,2.76,0,0,1-.54-.79,2.43,2.43,0,0,1-.19-1,2.4,2.4,0,0,1,.19-1A2.82,2.82,0,0,1,6.18,5,2.81,2.81,0,0,1,7,4.42a2.4,2.4,0,0,1,1-.19,2.43,2.43,0,0,1,1,.19A2.76,2.76,0,0,1,9.71,5a2.46,2.46,0,0,1,.54.8,2.4,2.4,0,0,1,.2,1,2.44,2.44,0,0,1-.2,1A2.59,2.59,0,0,1,8.92,9a2.44,2.44,0,0,1-1,.2A2.4,2.4,0,0,1,7,9,2.46,2.46,0,0,1,6.18,8.49Zm6.12,4.66a4.39,4.39,0,0,0-.18-.89,4.22,4.22,0,0,0-.57-1.19A4.24,4.24,0,0,0,9.44,9.48a3.41,3.41,0,0,0,.68-.5A3.36,3.36,0,0,0,11,7.56a3.32,3.32,0,0,0,.11-.83,3.09,3.09,0,0,0-.24-1.22,3.26,3.26,0,0,0-.67-1,3,3,0,0,0-1-.67A3,3,0,0,0,7.95,3.6a2.93,2.93,0,0,0-1.22.25A3.07,3.07,0,0,0,5.07,5.51a2.93,2.93,0,0,0-.25,1.22,3,3,0,0,0,.12.84,3,3,0,0,0,.32.76A3.3,3.3,0,0,0,5.78,9a3.06,3.06,0,0,0,.68.5,4.41,4.41,0,0,0-1.19.65,4.1,4.1,0,0,0-.91,1,4.18,4.18,0,0,0-.58,1.18,4.41,4.41,0,0,0-.18.8A6.66,6.66,0,0,1,1.28,8a6.72,6.72,0,1,1,11,5.15Z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>other_account_inverse_16x16</title><path class="cls-1" d="M7.92.29A7.71,7.71,0,1,0,15.64,8,7.73,7.73,0,0,0,7.92.29ZM4.12,13.54a4,4,0,0,1,.13-.93,3.69,3.69,0,0,1,1-1.66A3.56,3.56,0,0,1,6,10.37a4,4,0,0,1,.9-.38,4.17,4.17,0,0,1,1-.13,3.79,3.79,0,0,1,1.48.29,3.61,3.61,0,0,1,2,2,3.74,3.74,0,0,1,.29,1.47,6.62,6.62,0,0,1-3.7,1.12A6.71,6.71,0,0,1,4.12,13.54Zm2-5a2.76,2.76,0,0,1-.54-.79,2.43,2.43,0,0,1-.19-1,2.4,2.4,0,0,1,.19-1A2.82,2.82,0,0,1,6.1,5a2.81,2.81,0,0,1,.8-.54,2.4,2.4,0,0,1,1-.19,2.43,2.43,0,0,1,1,.19A2.76,2.76,0,0,1,9.63,5a2.46,2.46,0,0,1,.54.8,2.4,2.4,0,0,1,.2,1,2.44,2.44,0,0,1-.2,1A2.59,2.59,0,0,1,8.84,9a2.44,2.44,0,0,1-1,.2,2.4,2.4,0,0,1-1-.2A2.46,2.46,0,0,1,6.1,8.49Zm6.12,4.66a4.39,4.39,0,0,0-.18-.89,4.22,4.22,0,0,0-.57-1.19A4.24,4.24,0,0,0,9.36,9.49,3.41,3.41,0,0,0,10,9a3.36,3.36,0,0,0,.84-1.42A3.32,3.32,0,0,0,11,6.74a3.09,3.09,0,0,0-.24-1.22,3.26,3.26,0,0,0-.67-1,3,3,0,0,0-1-.67,3,3,0,0,0-1.22-.25,2.93,2.93,0,0,0-1.22.25A3.07,3.07,0,0,0,5,5.51a2.93,2.93,0,0,0-.25,1.22,3,3,0,0,0,.12.84,3,3,0,0,0,.32.76A3.3,3.3,0,0,0,5.7,9a3.06,3.06,0,0,0,.68.5,4.41,4.41,0,0,0-1.19.65,4.1,4.1,0,0,0-.91,1,4.18,4.18,0,0,0-.58,1.18,4.41,4.41,0,0,0-.18.8A6.66,6.66,0,0,1,1.2,8a6.72,6.72,0,1,1,11,5.15Z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,102 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as data from 'data';
import * as nls from 'vscode-nls';
import { Settings } from './interfaces';
const localize = nls.loadMessageBundle();
const publicAzureSettings = {
configKey: 'accounts.azure.enablePublicCloud',
metadata: <data.AccountProviderMetadata> {
displayName: localize('publicCloudDisplayName', 'Azure'),
id: 'azurePublicCloud',
settings: <Settings>{
host: 'https://login.microsoftonline.com',
clientId: 'TBD',
signInResourceId: 'https://management.core.windows.net/',
graphResource: {
id: 'https://graph.windows.net/',
endpoint: 'https://graph.windows.net'
},
armResource: {
id: 'https://management.core.windows.net/',
endpoint: 'https://management.azure.com'
},
redirectUri: 'http://localhost/redirect'
}
}
};
const usGovAzureSettings = {
configKey: 'accounts.azure.enableUsGovCloud',
metadata: <data.AccountProviderMetadata> {
displayName: localize('usGovCloudDisplayName', 'Azure (US Government)'),
id: 'usGovAzureCloud',
settings: <Settings> {
host: 'https://login.microsoftonline.com/',
clientId: 'TBD',
signInResourceId: 'https://management.core.usgovcloudapi.net/',
graphResource: {
id: 'https://graph.usgovcloudapi.net/',
endpoint: 'https://graph.usgovcloudapi.net'
},
armResource: {
id: 'https://management.core.usgovcloudapi.net/',
endpoint: 'https://management.usgovcloudapi.net'
},
redirectUri: 'http://localhost/redirect'
}
}
};
const chinaAzureSettings = {
configKey: 'accounts.azure.enableChinaCloud',
metadata: <data.AccountProviderMetadata> {
displayName: localize('chinaCloudDisplayName', 'Azure (China)'),
id: 'chinaAzureCloud',
settings: <Settings> {
host: 'https://login.chinacloudapi.cn/',
clientId: 'TBD',
signInResourceId: 'https://management.core.chinacloudapi.cn/',
graphResource: {
id: 'https://graph.chinacloudapi.cn/',
endpoint: 'https://graph.chinacloudapi.cn'
},
armResource: {
id: 'https://management.core.chinacloudapi.cn/',
endpoint: 'https://managemement.chinacloudapi.net'
},
redirectUri: 'http://localhost/redirect'
}
}
};
const germanyAzureSettings = {
configKey: 'accounts.azure.enableGermanyCloud',
metadata: <data.AccountProviderMetadata> {
displayName: localize('germanyCloud', 'Azure (Germany)'),
id: 'germanyAzureCloud',
settings: <Settings> {
host: 'https://login.microsoftazure.de/',
clientId: 'TBD',
signInResourceId: 'https://management.core.cloudapi.de/',
graphResource: {
id: 'https://graph.cloudapi.de/',
endpoint: 'https://graph.cloudapi.de'
},
armResource: {
id: 'https://management.core.cloudapi.de/',
endpoint: 'https://management.microsoftazure.de'
},
redirectUri: 'http://localhost/redirect'
}
}
};
export default [publicAzureSettings/*, chinaAzureSettings, germanyAzureSettings, usGovAzureSettings*/];

View File

@@ -0,0 +1,244 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as adal from 'adal-node';
import * as data from 'data';
import * as crypto from 'crypto';
import * as fs from 'fs';
export default class TokenCache implements adal.TokenCache {
private static CipherAlgorithm = 'aes256';
private static CipherAlgorithmIvLength = 16;
private static CipherKeyLength = 32;
private static FsOptions = { encoding: 'ascii' };
private _activeOperation: Thenable<any>;
constructor(
private _credentialProvider: data.CredentialProvider,
private _credentialServiceKey: string,
private _cacheSerializationPath: string
) {
}
// PUBLIC METHODS //////////////////////////////////////////////////////
public add(entries: adal.TokenCacheEntry[], callback: (error?: Error) => void): void {
let self = this;
this.doOperation(() => {
return self.readCache()
.then(cache => self.addToCache(cache, entries))
.then(updatedCache => self.writeCache(updatedCache))
.then(
() => callback(null),
(err) => callback(err)
);
});
}
public clear(): Thenable<void> {
let self = this;
// 1) Delete encrypted serialization file
// If we got an 'ENOENT' response, the file doesn't exist, which is fine
// 3) Delete the encryption key
return new Promise<void>((resolve, reject) => {
fs.unlink(self._cacheSerializationPath, err => {
if (err && err.code !== 'ENOENT') {
reject(err);
} else {
resolve();
}
});
})
.then(() => { return self._credentialProvider.deleteCredential(self._credentialServiceKey); })
.then(() => {});
}
public find(query: adal.TokenCacheQuery, callback: (error: Error, results: adal.TokenCacheEntry[]) => void): void {
let self = this;
this.doOperation(() => {
return self.readCache()
.then(cache => {
return cache.filter(
entry => TokenCache.findByPartial(entry, query)
);
})
.then(
results => callback(null, results),
(err) => callback(err, null)
);
});
}
public remove(entries: adal.TokenCacheEntry[], callback: (error?: Error) => void): void {
let self = this;
this.doOperation(() => {
return this.readCache()
.then(cache => self.removeFromCache(cache, entries))
.then(updatedCache => self.writeCache(updatedCache))
.then(
() => callback(null),
(err) => callback(err)
);
});
}
// PRIVATE METHODS /////////////////////////////////////////////////////
private static findByKeyHelper(entry1: adal.TokenCacheEntry, entry2: adal.TokenCacheEntry): boolean {
return entry1._authority === entry2._authority
&& entry1._clientId === entry2._clientId
&& entry1.userId === entry2.userId
&& entry1.resource === entry2.resource;
}
private static findByPartial(entry: adal.TokenCacheEntry, query: object): boolean {
for (let key in query) {
if (entry[key] === undefined || entry[key] !== query[key]) {
return false;
}
}
return true;
}
private doOperation<T>(op: () => Thenable<T>): void {
// Initialize the active operation to an empty promise if necessary
let activeOperation = this._activeOperation || Promise.resolve<any>(null);
// Chain the operation to perform to the end of the existing promise
activeOperation = activeOperation.then(op);
// Add a catch at the end to make sure we can continue after any errors
activeOperation = activeOperation.then(null, err => {
console.error(`Failed to perform token cache operation: ${err}`);
});
// Point the current active operation to this one
this._activeOperation = activeOperation;
}
private addToCache(cache: adal.TokenCacheEntry[], entries: adal.TokenCacheEntry[]): adal.TokenCacheEntry[] {
// First remove entries from the db that are being updated
cache = this.removeFromCache(cache, entries);
// Then add the new entries to the cache
entries.forEach((entry: adal.TokenCacheEntry) => {
cache.push(entry);
});
return cache;
}
private getOrCreateEncryptionParams(): Thenable<EncryptionParams> {
let self = this;
return this._credentialProvider.readCredential(this._credentialServiceKey)
.then(credential => {
if (credential.password) {
// We already have encryption params, deserialize them
let splitValues = credential.password.split('|');
if (splitValues.length === 2 && splitValues[0] && splitValues[1]) {
try {
return <EncryptionParams>{
key: new Buffer(splitValues[0], 'hex'),
initializationVector: new Buffer(splitValues[1], 'hex')
};
} catch(e) {
// Swallow the error and fall through to generate new params
console.warn('Failed to deserialize encryption params, new ones will be generated.');
}
}
}
// We haven't stored encryption values, so generate them
let encryptKey = crypto.randomBytes(TokenCache.CipherKeyLength);
let initializationVector = crypto.randomBytes(TokenCache.CipherAlgorithmIvLength);
// Serialize the values
let serializedValues = `${encryptKey.toString('hex')}|${initializationVector.toString('hex')}`;
return self._credentialProvider.saveCredential(self._credentialServiceKey, serializedValues)
.then(() => {
return <EncryptionParams> {
key: encryptKey,
initializationVector: initializationVector
};
});
});
}
private readCache(): Thenable<adal.TokenCacheEntry[]> {
let self = this;
// NOTE: File system operations are performed synchronously to avoid annoying nested callbacks
// 1) Get the encryption key
// 2) Read the encrypted token cache file
// 3) Decrypt the file contents
// 4) Deserialize and return
return this.getOrCreateEncryptionParams()
.then(encryptionParams => {
try {
let cacheCipher = fs.readFileSync(self._cacheSerializationPath, TokenCache.FsOptions);
let decipher = crypto.createDecipheriv(TokenCache.CipherAlgorithm, encryptionParams.key, encryptionParams.initializationVector);
let cacheJson = decipher.update(cacheCipher, 'hex', 'binary');
cacheJson += decipher.final('binary');
return JSON.parse(cacheJson);
} catch(e) {
throw e;
}
})
.then(null, err => {
// If reading the token cache fails, we'll just assume the tokens are garbage
console.warn(`Failed to read token cache: ${err}`);
return [];
});
}
private removeFromCache(cache: adal.TokenCacheEntry[], entries: adal.TokenCacheEntry[]): adal.TokenCacheEntry[] {
entries.forEach((entry: adal.TokenCacheEntry) => {
// Check to see if the entry exists
let match = cache.findIndex(entry2 => TokenCache.findByKeyHelper(entry, entry2));
if (match >= 0) {
// Entry exists, remove it from cache
cache.splice(match, 1);
}
});
return cache;
}
private writeCache(cache: adal.TokenCacheEntry[]): Thenable<void> {
let self = this;
// NOTE: File system operations are being done synchronously to avoid annoying callback nesting
// 1) Get (or generate) the encryption key
// 2) Stringify the token cache entries
// 4) Encrypt the JSON
// 3) Write to the file
return this.getOrCreateEncryptionParams()
.then(encryptionParams => {
try {
let cacheJson = JSON.stringify(cache);
let cipher = crypto.createCipheriv(TokenCache.CipherAlgorithm, encryptionParams.key, encryptionParams.initializationVector);
let cacheCipher = cipher.update(cacheJson, 'binary', 'hex');
cacheCipher += cipher.final('hex');
fs.writeFileSync(self._cacheSerializationPath, cacheCipher, TokenCache.FsOptions);
} catch (e) {
throw e;
}
});
}
}
interface EncryptionParams {
key: Buffer;
initializationVector: Buffer;
}

View File

@@ -0,0 +1,12 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vscode-nls';
let localize = nls.loadMessageBundle();
export const extensionName = localize('extensionName', 'Azure Accounts');

View File

@@ -0,0 +1,35 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as fs from 'fs';
import * as path from 'path';
import * as vscode from 'vscode';
import { Utils } from 'extensions-modules';
import * as constants from './constants';
import { AzureAccountProviderService } from './account-provider/azureAccountProviderService';
// EXTENSION ACTIVATION ////////////////////////////////////////////////////
export function activate(context: vscode.ExtensionContext): void {
// Create the folder for storing the token caches
let storagePath = path.join(Utils.getDefaultLogLocation(), constants.extensionName);
try {
if (!fs.existsSync(storagePath)) {
fs.mkdirSync(storagePath);
console.log('Initialized Azure account extension storage.');
}
} catch(e) {
console.error(`Initialization of Azure account extension storage failed: ${e}`);
console.error('Azure accounts will not be available');
return;
}
// Create the provider service and activate
const accountProviderService = new AzureAccountProviderService(context, storagePath);
context.subscriptions.push(accountProviderService);
accountProviderService.activate();
}

View File

@@ -0,0 +1,7 @@
/*---------------------------------------------------------------------------------------------
* 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/sql/data.d.ts'/>

View File

@@ -0,0 +1,20 @@
{
"compileOnSave": true,
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"outDir": "./out",
"lib": [
"es6", "es2015.promise"
],
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"declaration": true,
"types": ["node"]
},
"exclude": [
"./node_modules"
]
}

View File

@@ -0,0 +1 @@
test/**

View File

@@ -0,0 +1,22 @@
// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS:
[{
"name": "sublimehq/Packages",
"version": "0.0.0",
"license": "TextMate Bundle License",
"repositoryURL": "https://github.com/sublimehq/Packages",
"licenseDetail": [
"Copyright (c) Sublime Packages project authors",
"",
"If not otherwise specified (see below), files in this folder fall under the following license: ",
"",
"Permission to copy, use, modify, sell and distribute this",
"software is granted. This software is provided \"as is\" without",
"express or implied warranty, and with no claim as to its",
"suitability for any purpose.",
"",
"An exception is made for files in readable text which contain their own license information, ",
"or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added ",
"to the base-name name of the original file, and an extension of txt, html, or similar. For example ",
"\"tidy\" is accompanied by \"tidy-license.txt\"."
]
}]

View File

@@ -0,0 +1,22 @@
{
"comments": {
"lineComment": "REM"
},
"brackets": [
["{", "}"],
["[", "]"],
["(", ")"]
],
"autoClosingPairs": [
["{", "}"],
["[", "]"],
["(", ")"],
["\"", "\""]
],
"surroundingPairs": [
["{", "}"],
["[", "]"],
["(", ")"],
["\"", "\""]
]
}

View File

@@ -0,0 +1,19 @@
{
"name": "bat",
"version": "0.1.0",
"publisher": "vscode",
"engines": { "vscode": "*" },
"contributes": {
"languages": [{
"id": "bat",
"extensions": [ ".bat", ".cmd"],
"aliases": [ "Batch", "bat" ],
"configuration": "./language-configuration.json"
}],
"grammars": [{
"language": "bat",
"scopeName": "source.dosbatch",
"path": "./syntaxes/Batch File.tmLanguage"
}]
}
}

View File

@@ -0,0 +1,169 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>uuid</key>
<string>E07EC438-7B75-4437-8AA1-DA94C1E6EACC</string>
<key>patterns</key>
<array>
<dict>
<key>name</key>
<string>keyword.command.dosbatch</string>
<key>match</key>
<string>\b(?i)(?:append|assoc|at|attrib|break|cacls|cd|chcp|chdir|chkdsk|chkntfs|cls|cmd|color|comp|compact|convert|copy|date|del|dir|diskcomp|diskcopy|doskey|echo|endlocal|erase|fc|find|findstr|format|ftype|graftabl|help|keyb|label|md|mkdir|mode|more|move|path|pause|popd|print|prompt|pushd|rd|recover|ren|rename|replace|restore|rmdir|set|setlocal|shift|sort|start|subst|time|title|tree|type|ver|verify|vol|xcopy)\b</string>
</dict>
<dict>
<key>name</key>
<string>keyword.control.statement.dosbatch</string>
<key>match</key>
<string>\b(?i)(?:goto|call|exit)\b</string>
</dict>
<dict>
<key>name</key>
<string>keyword.control.conditional.if.dosbatch</string>
<key>match</key>
<string>\b(?i)if\s+((not)\s+)(exist|defined|errorlevel|cmdextversion)\b</string>
</dict>
<dict>
<key>name</key>
<string>keyword.control.conditional.dosbatch</string>
<key>match</key>
<string>\b(?i)(?:if|else)\b</string>
</dict>
<dict>
<key>name</key>
<string>keyword.control.repeat.dosbatch</string>
<key>match</key>
<string>\b(?i)for\b</string>
</dict>
<dict>
<key>name</key>
<string>keyword.operator.dosbatch</string>
<key>match</key>
<string>\b(?:EQU|NEQ|LSS|LEQ|GTR|GEQ)\b</string>
</dict>
<dict>
<key>name</key>
<string>comment.line.rem.dosbatch</string>
<key>match</key>
<string>\b(?i)rem(?:$|\s.*$)</string>
</dict>
<dict>
<key>name</key>
<string>comment.line.colons.dosbatch</string>
<key>match</key>
<string>\s*:\s*:.*$</string>
</dict>
<dict>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>variable.parameter.function.begin.shell</string>
</dict>
</dict>
<key>name</key>
<string>variable.parameter.function.dosbatch</string>
<key>match</key>
<string>(?i)(%)(~(?:f|d|p|n|x|s|a|t|z|\$[^:]*:)*)?\d</string>
</dict>
<dict>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>variable.other.parsetime.begin.shell</string>
</dict>
<key>2</key>
<dict>
<key>name</key>
<string>variable.other.parsetime.end.shell</string>
</dict>
</dict>
<key>name</key>
<string>variable.other.parsetime.dosbatch</string>
<key>match</key>
<string>(%)[^%]+(%)|(%%)[^%]+(%%)</string>
</dict>
<dict>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>variable.parameter.loop.begin.shell</string>
</dict>
</dict>
<key>name</key>
<string>variable.parameter.loop.dosbatch</string>
<key>match</key>
<string>(?i)(%%)(~(?:f|d|p|n|x|s|a|t|z|\$[^:]*:)*)?[a-z]</string>
</dict>
<dict>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>variable.other.delayed.begin.shell</string>
</dict>
<key>2</key>
<dict>
<key>name</key>
<string>variable.other.delayed.end.shell</string>
</dict>
</dict>
<key>name</key>
<string>variable.other.delayed.dosbatch</string>
<key>match</key>
<string>(!)[^!]+(!)</string>
</dict>
<dict>
<key>begin</key>
<string>"</string>
<key>endCaptures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.end.shell</string>
</dict>
</dict>
<key>beginCaptures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.begin.shell</string>
</dict>
</dict>
<key>name</key>
<string>string.quoted.double.dosbatch</string>
<key>end</key>
<string>"|$</string>
</dict>
<dict>
<key>name</key>
<string>keyword.operator.pipe.dosbatch</string>
<key>match</key>
<string>[|]</string>
</dict>
<dict>
<key>name</key>
<string>keyword.operator.redirect.shell</string>
<key>match</key>
<string>&amp;&gt;|\d*&gt;&amp;\d*|\d*(&gt;&gt;|&gt;|&lt;)|\d*&lt;&amp;|\d*&lt;&gt;</string>
</dict>
</array>
<key>name</key>
<string>Batch File</string>
<key>scopeName</key>
<string>source.dosbatch</string>
<key>fileTypes</key>
<array>
<string>bat</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,24 @@
@echo off
setlocal
title VSCode Dev
pushd %~dp0\..
:: Node modules
if not exist node_modules call .\scripts\npm.bat install
:: Get electron
node .\node_modules\gulp\bin\gulp.js electron
:: Build
if not exist out node .\node_modules\gulp\bin\gulp.js compile
:: Configuration
set NODE_ENV=development
call echo %%LINE:rem +=%%
popd
endlocal

View File

@@ -0,0 +1,343 @@
[
{
"c": "@",
"t": "source.dosbatch",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "echo",
"t": "source.dosbatch keyword.command.dosbatch",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
"dark_vs": "keyword: #569CD6",
"light_vs": "keyword: #0000FF",
"hc_black": "keyword: #569CD6"
}
},
{
"c": " off",
"t": "source.dosbatch",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "setlocal",
"t": "source.dosbatch keyword.command.dosbatch",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
"dark_vs": "keyword: #569CD6",
"light_vs": "keyword: #0000FF",
"hc_black": "keyword: #569CD6"
}
},
{
"c": "title",
"t": "source.dosbatch keyword.command.dosbatch",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
"dark_vs": "keyword: #569CD6",
"light_vs": "keyword: #0000FF",
"hc_black": "keyword: #569CD6"
}
},
{
"c": " VSCode Dev",
"t": "source.dosbatch",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "pushd",
"t": "source.dosbatch keyword.command.dosbatch",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
"dark_vs": "keyword: #569CD6",
"light_vs": "keyword: #0000FF",
"hc_black": "keyword: #569CD6"
}
},
{
"c": " ",
"t": "source.dosbatch",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "%",
"t": "source.dosbatch variable.parameter.function.dosbatch variable.parameter.function.begin.shell",
"r": {
"dark_plus": "variable: #9CDCFE",
"light_plus": "variable: #001080",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "variable: #9CDCFE"
}
},
{
"c": "~dp0",
"t": "source.dosbatch variable.parameter.function.dosbatch",
"r": {
"dark_plus": "variable: #9CDCFE",
"light_plus": "variable: #001080",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "variable: #9CDCFE"
}
},
{
"c": "\\..",
"t": "source.dosbatch",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": ":: Node modules",
"t": "source.dosbatch comment.line.colons.dosbatch",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
"dark_vs": "comment: #608B4E",
"light_vs": "comment: #008000",
"hc_black": "comment: #7CA668"
}
},
{
"c": "if not exist",
"t": "source.dosbatch keyword.control.conditional.if.dosbatch",
"r": {
"dark_plus": "keyword.control: #C586C0",
"light_plus": "keyword.control: #AF00DB",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
}
},
{
"c": " node_modules ",
"t": "source.dosbatch",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "call",
"t": "source.dosbatch keyword.control.statement.dosbatch",
"r": {
"dark_plus": "keyword.control: #C586C0",
"light_plus": "keyword.control: #AF00DB",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
}
},
{
"c": " .\\scripts\\npm.bat install",
"t": "source.dosbatch",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": ":: Get electron",
"t": "source.dosbatch comment.line.colons.dosbatch",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
"dark_vs": "comment: #608B4E",
"light_vs": "comment: #008000",
"hc_black": "comment: #7CA668"
}
},
{
"c": "node .\\node_modules\\gulp\\bin\\gulp.js electron",
"t": "source.dosbatch",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": ":: Build",
"t": "source.dosbatch comment.line.colons.dosbatch",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
"dark_vs": "comment: #608B4E",
"light_vs": "comment: #008000",
"hc_black": "comment: #7CA668"
}
},
{
"c": "if not exist",
"t": "source.dosbatch keyword.control.conditional.if.dosbatch",
"r": {
"dark_plus": "keyword.control: #C586C0",
"light_plus": "keyword.control: #AF00DB",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
}
},
{
"c": " out node .\\node_modules\\gulp\\bin\\gulp.js compile",
"t": "source.dosbatch",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": ":: Configuration",
"t": "source.dosbatch comment.line.colons.dosbatch",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
"dark_vs": "comment: #608B4E",
"light_vs": "comment: #008000",
"hc_black": "comment: #7CA668"
}
},
{
"c": "set",
"t": "source.dosbatch keyword.command.dosbatch",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
"dark_vs": "keyword: #569CD6",
"light_vs": "keyword: #0000FF",
"hc_black": "keyword: #569CD6"
}
},
{
"c": " NODE_ENV=development",
"t": "source.dosbatch",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "call",
"t": "source.dosbatch keyword.control.statement.dosbatch",
"r": {
"dark_plus": "keyword.control: #C586C0",
"light_plus": "keyword.control: #AF00DB",
"dark_vs": "keyword.control: #569CD6",
"light_vs": "keyword.control: #0000FF",
"hc_black": "keyword.control: #C586C0"
}
},
{
"c": " ",
"t": "source.dosbatch",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "echo",
"t": "source.dosbatch keyword.command.dosbatch",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
"dark_vs": "keyword: #569CD6",
"light_vs": "keyword: #0000FF",
"hc_black": "keyword: #569CD6"
}
},
{
"c": " ",
"t": "source.dosbatch",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "%%LINE:rem +=%%",
"t": "source.dosbatch variable.other.parsetime.dosbatch",
"r": {
"dark_plus": "variable: #9CDCFE",
"light_plus": "variable: #001080",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "variable: #9CDCFE"
}
},
{
"c": "popd",
"t": "source.dosbatch keyword.command.dosbatch",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
"dark_vs": "keyword: #569CD6",
"light_vs": "keyword: #0000FF",
"hc_black": "keyword: #569CD6"
}
},
{
"c": "endlocal",
"t": "source.dosbatch keyword.command.dosbatch",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
"dark_vs": "keyword: #569CD6",
"light_vs": "keyword: #0000FF",
"hc_black": "keyword: #569CD6"
}
}
]

View File

@@ -0,0 +1,4 @@
test/**
src/**
tsconfig.json
npm-shrinkwrap.json

View File

@@ -0,0 +1,15 @@
{
"name": "configuration-editing",
"version": "0.0.1",
"dependencies": {
"jsonc-parser": {
"version": "0.3.1",
"from": "jsonc-parser@0.3.1"
},
"vscode-nls": {
"version": "2.0.2",
"from": "vscode-nls@>=2.0.2 <3.0.0",
"resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz"
}
}
}

View File

@@ -0,0 +1,87 @@
{
"name": "configuration-editing",
"version": "0.0.1",
"publisher": "vscode",
"engines": {
"vscode": "^1.0.0"
},
"categories": [
"Languages",
"Other"
],
"activationEvents": [
"onLanguage:json"
],
"main": "./out/extension",
"scripts": {
"compile": "gulp compile-extension:configuration-editing",
"watch": "gulp watch-extension:configuration-editing"
},
"dependencies": {
"jsonc-parser": "^0.3.1",
"vscode-nls": "^2.0.1"
},
"contributes": {
"jsonValidation": [
{
"fileMatch": "vscode://defaultsettings/keybindings.json",
"url": "vscode://schemas/keybindings"
},
{
"fileMatch": "%APP_SETTINGS_HOME%/keybindings.json",
"url": "vscode://schemas/keybindings"
},
{
"fileMatch": "vscode://defaultsettings/settings.json",
"url": "vscode://schemas/settings"
},
{
"fileMatch": "vscode://defaultsettings/resourceSettings.json",
"url": "vscode://schemas/settings/resource"
},
{
"fileMatch": "vscode://settings/workspaceSettings.json",
"url": "vscode://schemas/settings"
},
{
"fileMatch": "%APP_SETTINGS_HOME%/settings.json",
"url": "vscode://schemas/settings"
},
{
"fileMatch": "%APP_WORKSPACES_HOME%/*/workspace.json",
"url": "vscode://schemas/workspaceConfig"
},
{
"fileMatch": "**/*.code-workspace",
"url": "vscode://schemas/workspaceConfig"
},
{
"fileMatch": "%APP_SETTINGS_HOME%/locale.json",
"url": "vscode://schemas/locale"
},
{
"fileMatch": "/.sqlops/settings.json",
"url": "vscode://schemas/settings"
},
{
"fileMatch": "/.sqlops/launch.json",
"url": "vscode://schemas/launch"
},
{
"fileMatch": "/.sqlops/tasks.json",
"url": "vscode://schemas/tasks"
},
{
"fileMatch": "%APP_SETTINGS_HOME%/snippets/*.json",
"url": "vscode://schemas/snippets"
},
{
"fileMatch": "/.vscode/extensions.json",
"url": "vscode://schemas/extensions"
}
]
},
"devDependencies": {
"@types/node": "^7.0.4"
}
}

View File

@@ -0,0 +1,183 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as vscode from 'vscode';
import { getLocation, visit, parse } from 'jsonc-parser';
import * as path from 'path';
import { SettingsDocument } from './settingsDocumentHelper';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
const decoration = vscode.window.createTextEditorDecorationType({
color: '#b1b1b1'
});
let pendingLaunchJsonDecoration: NodeJS.Timer;
export function activate(context): void {
//keybindings.json command-suggestions
context.subscriptions.push(registerKeybindingsCompletions());
//settings.json suggestions
context.subscriptions.push(registerSettingsCompletions());
//extensions.json suggestions
context.subscriptions.push(registerExtensionsCompletions());
// launch.json decorations
context.subscriptions.push(vscode.window.onDidChangeActiveTextEditor(editor => updateLaunchJsonDecorations(editor), null, context.subscriptions));
context.subscriptions.push(vscode.workspace.onDidChangeTextDocument(event => {
if (vscode.window.activeTextEditor && event.document === vscode.window.activeTextEditor.document) {
if (pendingLaunchJsonDecoration) {
clearTimeout(pendingLaunchJsonDecoration);
}
pendingLaunchJsonDecoration = setTimeout(() => updateLaunchJsonDecorations(vscode.window.activeTextEditor), 1000);
}
}, null, context.subscriptions));
updateLaunchJsonDecorations(vscode.window.activeTextEditor);
}
function registerKeybindingsCompletions(): vscode.Disposable {
const commands = vscode.commands.getCommands(true);
return vscode.languages.registerCompletionItemProvider({ pattern: '**/keybindings.json' }, {
provideCompletionItems(document, position, token) {
const location = getLocation(document.getText(), document.offsetAt(position));
if (location.path[1] === 'command') {
const range = document.getWordRangeAtPosition(position) || new vscode.Range(position, position);
return commands.then(ids => ids.map(id => newSimpleCompletionItem(JSON.stringify(id), range)));
}
}
});
}
function registerSettingsCompletions(): vscode.Disposable {
return vscode.languages.registerCompletionItemProvider({ language: 'json', pattern: '**/settings.json' }, {
provideCompletionItems(document, position, token) {
return new SettingsDocument(document).provideCompletionItems(position, token);
}
});
}
function registerExtensionsCompletions(): vscode.Disposable {
return vscode.languages.registerCompletionItemProvider({ pattern: '**/extensions.json' }, {
provideCompletionItems(document, position, token) {
const location = getLocation(document.getText(), document.offsetAt(position));
const range = document.getWordRangeAtPosition(position) || new vscode.Range(position, position);
if (location.path[0] === 'recommendations') {
const config = parse(document.getText());
const alreadyEnteredExtensions = config && config.recommendations || [];
if (Array.isArray(alreadyEnteredExtensions)) {
const knownExtensionProposals = vscode.extensions.all.filter(e =>
!(e.id.startsWith('vscode.')
|| e.id === 'Microsoft.vscode-markdown'
|| alreadyEnteredExtensions.indexOf(e.id) > -1));
if (knownExtensionProposals.length) {
return knownExtensionProposals.map(e => {
const item = new vscode.CompletionItem(e.id);
const insertText = `"${e.id}"`;
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"';
example.kind = vscode.CompletionItemKind.Value;
example.range = range;
return [example];
}
}
}
return [];
}
});
}
function newSimpleCompletionItem(label: string, range: vscode.Range, description?: string, insertText?: string): vscode.CompletionItem {
const item = new vscode.CompletionItem(label);
item.kind = vscode.CompletionItemKind.Value;
item.detail = description;
item.insertText = insertText || label;
item.range = range;
return item;
}
function updateLaunchJsonDecorations(editor: vscode.TextEditor | undefined): void {
if (!editor || path.basename(editor.document.fileName) !== 'launch.json') {
return;
}
const ranges: vscode.Range[] = [];
let addPropertyAndValue = false;
let depthInArray = 0;
visit(editor.document.getText(), {
onObjectProperty: (property, offset, length) => {
// Decorate attributes which are unlikely to be edited by the user.
// Only decorate "configurations" if it is not inside an array (compounds have a configurations property which should not be decorated).
addPropertyAndValue = property === 'version' || property === 'type' || property === 'request' || property === 'compounds' || (property === 'configurations' && depthInArray === 0);
if (addPropertyAndValue) {
ranges.push(new vscode.Range(editor.document.positionAt(offset), editor.document.positionAt(offset + length)));
}
},
onLiteralValue: (value, offset, length) => {
if (addPropertyAndValue) {
ranges.push(new vscode.Range(editor.document.positionAt(offset), editor.document.positionAt(offset + length)));
}
},
onArrayBegin: (offset: number, length: number) => {
depthInArray++;
},
onArrayEnd: (offset: number, length: number) => {
depthInArray--;
}
});
editor.setDecorations(decoration, ranges);
}
vscode.languages.registerDocumentSymbolProvider({ pattern: '**/launch.json', language: 'json' }, {
provideDocumentSymbols(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.ProviderResult<vscode.SymbolInformation[]> {
const result: vscode.SymbolInformation[] = [];
let name: string = '';
let lastProperty = '';
let startOffset = 0;
let depthInObjects = 0;
visit(document.getText(), {
onObjectProperty: (property, offset, length) => {
lastProperty = property;
},
onLiteralValue: (value: any, offset: number, length: number) => {
if (lastProperty === 'name') {
name = value;
}
},
onObjectBegin: (offset: number, length: number) => {
depthInObjects++;
if (depthInObjects === 2) {
startOffset = offset;
}
},
onObjectEnd: (offset: number, length: number) => {
if (name && depthInObjects === 2) {
result.push(new vscode.SymbolInformation(name, vscode.SymbolKind.Object, new vscode.Range(document.positionAt(startOffset), document.positionAt(offset))));
}
depthInObjects--;
},
});
return result;
}
});

View File

@@ -0,0 +1,209 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { getLocation, Location } from 'jsonc-parser';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
export class SettingsDocument {
constructor(private document: vscode.TextDocument) { }
public provideCompletionItems(position: vscode.Position, token: vscode.CancellationToken): vscode.ProviderResult<vscode.CompletionItem[]> {
const location = getLocation(this.document.getText(), this.document.offsetAt(position));
const range = this.document.getWordRangeAtPosition(position) || new vscode.Range(position, position);
// window.title
if (location.path[0] === 'window.title') {
return this.provideWindowTitleCompletionItems(location, range);
}
// files.association
if (location.path[0] === 'files.associations') {
return this.provideFilesAssociationsCompletionItems(location, range);
}
// files.exclude, search.exclude
if (location.path[0] === 'files.exclude' || location.path[0] === 'search.exclude') {
return this.provideExcludeCompletionItems(location, range);
}
// files.defaultLanguage
if (location.path[0] === 'files.defaultLanguage') {
return this.provideLanguageCompletionItems(location, range);
}
return this.provideLanguageOverridesCompletionItems(location, position);
}
private provideWindowTitleCompletionItems(location: Location, range: vscode.Range): vscode.ProviderResult<vscode.CompletionItem[]> {
const completions: vscode.CompletionItem[] = [];
completions.push(this.newSimpleCompletionItem('${activeEditorShort}', range, localize('activeEditorShort', "e.g. myFile.txt")));
completions.push(this.newSimpleCompletionItem('${activeEditorMedium}', range, localize('activeEditorMedium', "e.g. myFolder/myFile.txt")));
completions.push(this.newSimpleCompletionItem('${activeEditorLong}', range, localize('activeEditorLong', "e.g. /Users/Development/myProject/myFolder/myFile.txt")));
completions.push(this.newSimpleCompletionItem('${rootName}', range, localize('rootName', "e.g. myFolder1, myFolder2, myFolder3")));
completions.push(this.newSimpleCompletionItem('${rootPath}', range, localize('rootPath', "e.g. /Users/Development/myProject")));
completions.push(this.newSimpleCompletionItem('${folderName}', range, localize('folderName', "e.g. myFolder")));
completions.push(this.newSimpleCompletionItem('${folderPath}', range, localize('folderPath', "e.g. /Users/Development/myFolder")));
completions.push(this.newSimpleCompletionItem('${appName}', range, localize('appName', "e.g. VS Code")));
completions.push(this.newSimpleCompletionItem('${dirty}', range, localize('dirty', "a dirty indicator if the active editor is dirty")));
completions.push(this.newSimpleCompletionItem('${separator}', range, localize('separator', "a conditional separator (' - ') that only shows when surrounded by variables with values")));
return Promise.resolve(completions);
}
private provideFilesAssociationsCompletionItems(location: Location, range: vscode.Range): vscode.ProviderResult<vscode.CompletionItem[]> {
const completions: vscode.CompletionItem[] = [];
// Key
if (location.path.length === 1) {
completions.push(this.newSnippetCompletionItem({
label: localize('assocLabelFile', "Files with Extension"),
documentation: localize('assocDescriptionFile', "Map all files matching the glob pattern in their filename to the language with the given identifier."),
snippet: location.isAtPropertyKey ? '"*.${1:extension}": "${2:language}"' : '{ "*.${1:extension}": "${2:language}" }',
range
}));
completions.push(this.newSnippetCompletionItem({
label: localize('assocLabelPath', "Files with Path"),
documentation: localize('assocDescriptionPath', "Map all files matching the absolute path glob pattern in their path to the language with the given identifier."),
snippet: location.isAtPropertyKey ? '"/${1:path to file}/*.${2:extension}": "${3:language}"' : '{ "/${1:path to file}/*.${2:extension}": "${3:language}" }',
range
}));
}
// Value
else if (location.path.length === 2 && !location.isAtPropertyKey) {
return this.provideLanguageCompletionItems(location, range);
}
return Promise.resolve(completions);
}
private provideExcludeCompletionItems(location: Location, range: vscode.Range): vscode.ProviderResult<vscode.CompletionItem[]> {
const completions: vscode.CompletionItem[] = [];
// Key
if (location.path.length === 1) {
completions.push(this.newSnippetCompletionItem({
label: localize('fileLabel', "Files by Extension"),
documentation: localize('fileDescription', "Match all files of a specific file extension."),
snippet: location.isAtPropertyKey ? '"**/*.${1:extension}": true' : '{ "**/*.${1:extension}": true }',
range
}));
completions.push(this.newSnippetCompletionItem({
label: localize('filesLabel', "Files with Multiple Extensions"),
documentation: localize('filesDescription', "Match all files with any of the file extensions."),
snippet: location.isAtPropertyKey ? '"**/*.{ext1,ext2,ext3}": true' : '{ "**/*.{ext1,ext2,ext3}": true }',
range
}));
completions.push(this.newSnippetCompletionItem({
label: localize('derivedLabel', "Files with Siblings by Name"),
documentation: localize('derivedDescription', "Match files that have siblings with the same name but a different extension."),
snippet: location.isAtPropertyKey ? '"**/*.${1:source-extension}": { "when": "$(basename).${2:target-extension}" }' : '{ "**/*.${1:source-extension}": { "when": "$(basename).${2:target-extension}" } }',
range
}));
completions.push(this.newSnippetCompletionItem({
label: localize('topFolderLabel', "Folder by Name (Top Level)"),
documentation: localize('topFolderDescription', "Match a top level folder with a specific name."),
snippet: location.isAtPropertyKey ? '"${1:name}": true' : '{ "${1:name}": true }',
range
}));
completions.push(this.newSnippetCompletionItem({
label: localize('topFoldersLabel', "Folders with Multiple Names (Top Level)"),
documentation: localize('topFoldersDescription', "Match multiple top level folders."),
snippet: location.isAtPropertyKey ? '"{folder1,folder2,folder3}": true' : '{ "{folder1,folder2,folder3}": true }',
range
}));
completions.push(this.newSnippetCompletionItem({
label: localize('folderLabel', "Folder by Name (Any Location)"),
documentation: localize('folderDescription', "Match a folder with a specific name in any location."),
snippet: location.isAtPropertyKey ? '"**/${1:name}": true' : '{ "**/${1:name}": true }',
range
}));
}
// Value
else {
completions.push(this.newSimpleCompletionItem('false', range, localize('falseDescription', "Disable the pattern.")));
completions.push(this.newSimpleCompletionItem('true', range, localize('trueDescription', "Enable the pattern.")));
completions.push(this.newSnippetCompletionItem({
label: localize('derivedLabel', "Files with Siblings by Name"),
documentation: localize('siblingsDescription', "Match files that have siblings with the same name but a different extension."),
snippet: '{ "when": "$(basename).${1:extension}" }',
range
}));
}
return Promise.resolve(completions);
}
private provideLanguageCompletionItems(location: Location, range: vscode.Range, formatFunc: (string) => string = (l) => JSON.stringify(l)): vscode.ProviderResult<vscode.CompletionItem[]> {
return vscode.languages.getLanguages().then(languages => {
return languages.map(l => {
return this.newSimpleCompletionItem(formatFunc(l), range);
});
});
}
private provideLanguageOverridesCompletionItems(location: Location, position: vscode.Position): vscode.ProviderResult<vscode.CompletionItem[]> {
let range = this.document.getWordRangeAtPosition(position) || new vscode.Range(position, position);
const text = this.document.getText(range);
if (location.path.length === 0) {
let snippet = '"[${1:language}]": {\n\t"$0"\n}';
// Suggestion model word matching includes quotes,
// hence exclude the starting quote from the snippet and the range
// ending quote gets replaced
if (text && text.startsWith('"')) {
range = new vscode.Range(new vscode.Position(range.start.line, range.start.character + 1), range.end);
snippet = snippet.substring(1);
}
return Promise.resolve([this.newSnippetCompletionItem({
label: localize('languageSpecificEditorSettings', "Language specific editor settings"),
documentation: localize('languageSpecificEditorSettingsDescription', "Override editor settings for language"),
snippet,
range
})]);
}
if (location.path.length === 1 && location.previousNode && typeof location.previousNode.value === 'string' && location.previousNode.value.startsWith('[')) {
// Suggestion model word matching includes closed sqaure bracket and ending quote
// Hence include them in the proposal to replace
return this.provideLanguageCompletionItems(location, range, language => `"[${language}]"`);
}
return Promise.resolve([]);
}
private newSimpleCompletionItem(text: string, range: vscode.Range, description?: string, insertText?: string): vscode.CompletionItem {
const item = new vscode.CompletionItem(text);
item.kind = vscode.CompletionItemKind.Value;
item.detail = description;
item.insertText = insertText ? insertText : text;
item.range = range;
return item;
}
private newSnippetCompletionItem(o: { label: string; documentation?: string; snippet: string; range: vscode.Range; }): vscode.CompletionItem {
const item = new vscode.CompletionItem(o.label);
item.kind = vscode.CompletionItemKind.Value;
item.documentation = o.documentation;
item.insertText = new vscode.SnippetString(o.snippet);
item.range = o.range;
return item;
}
}

View File

@@ -0,0 +1,7 @@
/*---------------------------------------------------------------------------------------------
* 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 types='@types/node'/>

View File

@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"outDir": "./out",
"lib": [
"es2015"
],
"strictNullChecks": true
},
"include": [
"src/**/*"
]
}

107
extensions/declares.d.ts vendored Normal file
View File

@@ -0,0 +1,107 @@
/*---------------------------------------------------------------------------------------------
* 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/typings/thenable.d.ts" />
// Declaring the following because the code gets compiled with es5, which lack definitions for console and timers.
declare var console: {
assert(value: any, message?: string, ...optionalParams: any[]): void;
dir(obj: any, options?: { showHidden?: boolean, depth?: number, colors?: boolean }): void;
error(message?: any, ...optionalParams: any[]): void;
info(message?: any, ...optionalParams: any[]): void;
log(message?: any, ...optionalParams: any[]): void;
time(label: string): void;
timeEnd(label: string): void;
trace(message?: any, ...optionalParams: any[]): void;
warn(message?: any, ...optionalParams: any[]): void;
};
// ---- ES6 promise ------------------------------------------------------
/**
* Represents the completion of an asynchronous operation.
*/
interface Promise<T> extends Thenable<T> {
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult>(onfulfilled?: (value: T) => TResult | Thenable<TResult>, onrejected?: (reason: any) => TResult | Thenable<TResult>): Promise<TResult>;
then<TResult>(onfulfilled?: (value: T) => TResult | Thenable<TResult>, onrejected?: (reason: any) => void): Promise<TResult>;
/**
* Attaches a callback for only the rejection of the Promise.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of the callback.
*/
catch(onrejected?: (reason: any) => T | Thenable<T>): Promise<T>;
// [Symbol.toStringTag]: string;
}
interface PromiseConstructor {
// /**
// * A reference to the prototype.
// */
// prototype: Promise<any>;
/**
* Creates a new Promise.
* @param executor A callback used to initialize the promise. This callback is passed two arguments:
* a resolve callback used to resolve the promise with a value or the result of another promise,
* and a reject callback used to reject the promise with a provided reason or error.
*/
new <T>(executor: (resolve: (value?: T | Thenable<T>) => void, reject: (reason?: any) => void) => void): Promise<T>;
/**
* Creates a Promise that is resolved with an array of results when all of the provided Promises
* resolve, or rejected when any Promise is rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
all<T>(values: Array<T | Thenable<T>>): Promise<T[]>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T>(values: Array<T | Thenable<T>>): Promise<T>;
/**
* Creates a new rejected promise for the provided reason.
* @param reason The reason the promise was rejected.
* @returns A new rejected Promise.
*/
reject(reason: any): Promise<void>;
/**
* Creates a new rejected promise for the provided reason.
* @param reason The reason the promise was rejected.
* @returns A new rejected Promise.
*/
reject<T>(reason: any): Promise<T>;
/**
* Creates a new resolved promise for the provided value.
* @param value A promise.
* @returns A promise whose internal state matches the provided promise.
*/
resolve<T>(value: T | Thenable<T>): Promise<T>;
/**
* Creates a new resolved promise.
* @returns A resolved promise.
*/
resolve(): Promise<void>;
// [Symbol.species]: Function;
}
declare var Promise: PromiseConstructor;

View File

@@ -0,0 +1 @@
test/**

View File

@@ -0,0 +1,22 @@
// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS:
[{
"name": "textmate/diff.tmbundle",
"version": "0.0.0",
"license": "TextMate Bundle License",
"repositoryURL": "https://github.com/textmate/diff.tmbundle",
"licenseDetail": [
"Copyright (c) textmate-diff.tmbundle project authors",
"",
"If not otherwise specified (see below), files in this repository fall under the following license:",
"",
"Permission to copy, use, modify, sell and distribute this",
"software is granted. This software is provided \"as is\" without",
"express or implied warranty, and with no claim as to its",
"suitability for any purpose.",
"",
"An exception is made for files in readable text which contain their own license information,",
"or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added",
"to the base-name name of the original file, and an extension of txt, html, or similar. For example",
"\"tidy\" is accompanied by \"tidy-license.txt\"."
]
}]

View File

@@ -0,0 +1,11 @@
{
"comments": {
"lineComment": "//",
"blockComment": [ "/*", "*/" ]
},
"brackets": [
["{", "}"],
["[", "]"],
["(", ")"]
]
}

View File

@@ -0,0 +1,26 @@
{
"name": "diff",
"version": "0.1.0",
"publisher": "vscode",
"engines": { "vscode": "*" },
"scripts": {
"update-grammar": "node ../../build/npm/update-grammar.js textmate/diff.tmbundle Syntaxes/Diff.plist ./syntaxes/diff.tmLanguage.json"
},
"contributes": {
"languages": [
{
"id": "diff",
"aliases": ["Diff", "diff" ],
"extensions": [".diff", ".patch", ".rej"],
"configuration": "./language-configuration.json"
}
],
"grammars": [
{
"language": "diff",
"scopeName": "source.diff",
"path": "./syntaxes/diff.tmLanguage.json"
}
]
}
}

View File

@@ -0,0 +1,268 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>fileTypes</key>
<array>
<string>patch</string>
<string>diff</string>
<string>rej</string>
</array>
<key>firstLineMatch</key>
<string>(?x)^
(===\ modified\ file
|==== \s* // .+ \s - \s .+ \s+ ====
|Index:\
|---\ [^%\n]
|\*\*\*.*\d{4}\s*$
|\d+(,\d+)* (a|d|c) \d+(,\d+)* $
|diff\ --git\
|commit\ [0-9a-f]{40}$
)</string>
<key>keyEquivalent</key>
<string>^~D</string>
<key>name</key>
<string>Diff</string>
<key>patterns</key>
<array>
<dict>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>punctuation.definition.separator.diff</string>
</dict>
</dict>
<key>match</key>
<string>^((\*{15})|(={67})|(-{3}))$\n?</string>
<key>name</key>
<string>meta.separator.diff</string>
</dict>
<dict>
<key>match</key>
<string>^\d+(,\d+)*(a|d|c)\d+(,\d+)*$\n?</string>
<key>name</key>
<string>meta.diff.range.normal</string>
</dict>
<dict>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>punctuation.definition.range.diff</string>
</dict>
<key>2</key>
<dict>
<key>name</key>
<string>meta.toc-list.line-number.diff</string>
</dict>
<key>3</key>
<dict>
<key>name</key>
<string>punctuation.definition.range.diff</string>
</dict>
</dict>
<key>match</key>
<string>^(@@)\s*(.+?)\s*(@@)($\n?)?</string>
<key>name</key>
<string>meta.diff.range.unified</string>
</dict>
<dict>
<key>captures</key>
<dict>
<key>3</key>
<dict>
<key>name</key>
<string>punctuation.definition.range.diff</string>
</dict>
<key>4</key>
<dict>
<key>name</key>
<string>punctuation.definition.range.diff</string>
</dict>
<key>6</key>
<dict>
<key>name</key>
<string>punctuation.definition.range.diff</string>
</dict>
<key>7</key>
<dict>
<key>name</key>
<string>punctuation.definition.range.diff</string>
</dict>
</dict>
<key>match</key>
<string>^(((\-{3}) .+ (\-{4}))|((\*{3}) .+ (\*{4})))$\n?</string>
<key>name</key>
<string>meta.diff.range.context</string>
</dict>
<dict>
<key>match</key>
<string>^diff --git a/.*$\n?</string>
<key>name</key>
<string>meta.diff.header.git</string>
</dict>
<dict>
<key>match</key>
<string>^diff (-|\S+\s+\S+).*$\n?</string>
<key>name</key>
<string>meta.diff.header.command</string>
</dict>
<dict>
<key>captures</key>
<dict>
<key>4</key>
<dict>
<key>name</key>
<string>punctuation.definition.from-file.diff</string>
</dict>
<key>6</key>
<dict>
<key>name</key>
<string>punctuation.definition.from-file.diff</string>
</dict>
<key>7</key>
<dict>
<key>name</key>
<string>punctuation.definition.from-file.diff</string>
</dict>
</dict>
<key>match</key>
<string>(^(((-{3}) .+)|((\*{3}) .+))$\n?|^(={4}) .+(?= - ))</string>
<key>name</key>
<string>meta.diff.header.from-file</string>
</dict>
<dict>
<key>captures</key>
<dict>
<key>2</key>
<dict>
<key>name</key>
<string>punctuation.definition.to-file.diff</string>
</dict>
<key>3</key>
<dict>
<key>name</key>
<string>punctuation.definition.to-file.diff</string>
</dict>
<key>4</key>
<dict>
<key>name</key>
<string>punctuation.definition.to-file.diff</string>
</dict>
</dict>
<key>match</key>
<string>(^(\+{3}) .+$\n?| (-) .* (={4})$\n?)</string>
<key>name</key>
<string>meta.diff.header.to-file</string>
</dict>
<dict>
<key>captures</key>
<dict>
<key>3</key>
<dict>
<key>name</key>
<string>punctuation.definition.inserted.diff</string>
</dict>
<key>6</key>
<dict>
<key>name</key>
<string>punctuation.definition.inserted.diff</string>
</dict>
</dict>
<key>match</key>
<string>^(((&gt;)( .*)?)|((\+).*))$\n?</string>
<key>name</key>
<string>markup.inserted.diff</string>
</dict>
<dict>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>punctuation.definition.inserted.diff</string>
</dict>
</dict>
<key>match</key>
<string>^(!).*$\n?</string>
<key>name</key>
<string>markup.changed.diff</string>
</dict>
<dict>
<key>captures</key>
<dict>
<key>3</key>
<dict>
<key>name</key>
<string>punctuation.definition.inserted.diff</string>
</dict>
<key>6</key>
<dict>
<key>name</key>
<string>punctuation.definition.inserted.diff</string>
</dict>
</dict>
<key>match</key>
<string>^(((&lt;)( .*)?)|((-).*))$\n?</string>
<key>name</key>
<string>markup.deleted.diff</string>
</dict>
<dict>
<key>begin</key>
<string>^(#)</string>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>punctuation.definition.comment.diff</string>
</dict>
</dict>
<key>comment</key>
<string>Git produces unified diffs with embedded comments"</string>
<key>end</key>
<string>\n</string>
<key>name</key>
<string>comment.line.number-sign.diff</string>
</dict>
<dict>
<key>match</key>
<string>^index [0-9a-f]{7,40}\.\.[0-9a-f]{7,40}.*$\n?</string>
<key>name</key>
<string>meta.diff.index.git</string>
</dict>
<dict>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>punctuation.separator.key-value.diff</string>
</dict>
<key>2</key>
<dict>
<key>name</key>
<string>meta.toc-list.file-name.diff</string>
</dict>
</dict>
<key>match</key>
<string>^Index(:) (.+)$\n?</string>
<key>name</key>
<string>meta.diff.index</string>
</dict>
<dict>
<key>match</key>
<string>^Only in .*: .*$\n?</string>
<key>name</key>
<string>meta.diff.only-in</string>
</dict>
</array>
<key>scopeName</key>
<string>source.diff</string>
<key>uuid</key>
<string>7E848FF4-708E-11D9-97B4-0011242E4184</string>
</dict>
</plist>

View File

@@ -0,0 +1,168 @@
{
"information_for_contributors": [
"This file has been converted from https://github.com/textmate/diff.tmbundle/blob/master/Syntaxes/Diff.plist",
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
"Once accepted there, we are happy to receive an update request."
],
"version": "https://github.com/textmate/diff.tmbundle/commit/0593bb775eab1824af97ef2172fd38822abd97d7",
"fileTypes": [
"patch",
"diff",
"rej"
],
"firstLineMatch": "(?x)^\n\t\t(===\\ modified\\ file\n\t\t|==== \\s* // .+ \\s - \\s .+ \\s+ ====\n\t\t|Index:\\ \n\t\t|---\\ [^%\\n]\n\t\t|\\*\\*\\*.*\\d{4}\\s*$\n\t\t|\\d+(,\\d+)* (a|d|c) \\d+(,\\d+)* $\n\t\t|diff\\ --git\\ \n\t\t|commit\\ [0-9a-f]{40}$\n\t\t)",
"keyEquivalent": "^~D",
"name": "Diff",
"patterns": [
{
"captures": {
"1": {
"name": "punctuation.definition.separator.diff"
}
},
"match": "^((\\*{15})|(={67})|(-{3}))$\\n?",
"name": "meta.separator.diff"
},
{
"match": "^\\d+(,\\d+)*(a|d|c)\\d+(,\\d+)*$\\n?",
"name": "meta.diff.range.normal"
},
{
"captures": {
"1": {
"name": "punctuation.definition.range.diff"
},
"2": {
"name": "meta.toc-list.line-number.diff"
},
"3": {
"name": "punctuation.definition.range.diff"
}
},
"match": "^(@@)\\s*(.+?)\\s*(@@)($\\n?)?",
"name": "meta.diff.range.unified"
},
{
"captures": {
"3": {
"name": "punctuation.definition.range.diff"
},
"4": {
"name": "punctuation.definition.range.diff"
},
"6": {
"name": "punctuation.definition.range.diff"
},
"7": {
"name": "punctuation.definition.range.diff"
}
},
"match": "^(((\\-{3}) .+ (\\-{4}))|((\\*{3}) .+ (\\*{4})))$\\n?",
"name": "meta.diff.range.context"
},
{
"match": "^diff --git a/.*$\\n?",
"name": "meta.diff.header.git"
},
{
"match": "^diff (-|\\S+\\s+\\S+).*$\\n?",
"name": "meta.diff.header.command"
},
{
"captures": {
"4": {
"name": "punctuation.definition.from-file.diff"
},
"6": {
"name": "punctuation.definition.from-file.diff"
},
"7": {
"name": "punctuation.definition.from-file.diff"
}
},
"match": "(^(((-{3}) .+)|((\\*{3}) .+))$\\n?|^(={4}) .+(?= - ))",
"name": "meta.diff.header.from-file"
},
{
"captures": {
"2": {
"name": "punctuation.definition.to-file.diff"
},
"3": {
"name": "punctuation.definition.to-file.diff"
},
"4": {
"name": "punctuation.definition.to-file.diff"
}
},
"match": "(^(\\+{3}) .+$\\n?| (-) .* (={4})$\\n?)",
"name": "meta.diff.header.to-file"
},
{
"captures": {
"3": {
"name": "punctuation.definition.inserted.diff"
},
"6": {
"name": "punctuation.definition.inserted.diff"
}
},
"match": "^(((>)( .*)?)|((\\+).*))$\\n?",
"name": "markup.inserted.diff"
},
{
"captures": {
"1": {
"name": "punctuation.definition.changed.diff"
}
},
"match": "^(!).*$\\n?",
"name": "markup.changed.diff"
},
{
"captures": {
"3": {
"name": "punctuation.definition.deleted.diff"
},
"6": {
"name": "punctuation.definition.deleted.diff"
}
},
"match": "^(((<)( .*)?)|((-).*))$\\n?",
"name": "markup.deleted.diff"
},
{
"begin": "^(#)",
"captures": {
"1": {
"name": "punctuation.definition.comment.diff"
}
},
"comment": "Git produces unified diffs with embedded comments\"",
"end": "\\n",
"name": "comment.line.number-sign.diff"
},
{
"match": "^index [0-9a-f]{7,40}\\.\\.[0-9a-f]{7,40}.*$\\n?",
"name": "meta.diff.index.git"
},
{
"captures": {
"1": {
"name": "punctuation.separator.key-value.diff"
},
"2": {
"name": "meta.toc-list.file-name.diff"
}
},
"match": "^Index(:) (.+)$\\n?",
"name": "meta.diff.index"
},
{
"match": "^Only in .*: .*$\\n?",
"name": "meta.diff.only-in"
}
],
"scopeName": "source.diff",
"uuid": "7E848FF4-708E-11D9-97B4-0011242E4184"
}

View File

@@ -0,0 +1,19 @@
--- lao Sat Jan 26 23:30:39 1991
+++ tzu Sat Jan 26 23:30:50 1991
@@ -1,7 +1,6 @@
-The Way that can be told of is not the eternal Way;
-The name that can be named is not the eternal name.
The Nameless is the origin of Heaven and Earth;
-The Named is the mother of all things.
+The named is the mother of all things.
+
Therefore let there always be non-being,
so we may see their subtlety,
And let there always be being,
@@ -9,3 +8,6 @@
The two are the same,
But after they are produced,
they have different names.
+They both may be called deep and profound.
+Deeper and more profound,
+The door of all subtleties!

View File

@@ -0,0 +1,398 @@
[
{
"c": "---",
"t": "source.diff meta.diff.header.from-file punctuation.definition.from-file.diff",
"r": {
"dark_plus": "meta.diff.header: #569CD6",
"light_plus": "meta.diff.header: #000080",
"dark_vs": "meta.diff.header: #569CD6",
"light_vs": "meta.diff.header: #000080",
"hc_black": "meta.diff.header: #000080"
}
},
{
"c": " lao\tSat Jan 26 23:30:39 1991",
"t": "source.diff meta.diff.header.from-file",
"r": {
"dark_plus": "meta.diff.header: #569CD6",
"light_plus": "meta.diff.header: #000080",
"dark_vs": "meta.diff.header: #569CD6",
"light_vs": "meta.diff.header: #000080",
"hc_black": "meta.diff.header: #000080"
}
},
{
"c": "+++",
"t": "source.diff meta.diff.header.to-file punctuation.definition.to-file.diff",
"r": {
"dark_plus": "meta.diff.header: #569CD6",
"light_plus": "meta.diff.header: #000080",
"dark_vs": "meta.diff.header: #569CD6",
"light_vs": "meta.diff.header: #000080",
"hc_black": "meta.diff.header: #000080"
}
},
{
"c": " tzu\tSat Jan 26 23:30:50 1991",
"t": "source.diff meta.diff.header.to-file",
"r": {
"dark_plus": "meta.diff.header: #569CD6",
"light_plus": "meta.diff.header: #000080",
"dark_vs": "meta.diff.header: #569CD6",
"light_vs": "meta.diff.header: #000080",
"hc_black": "meta.diff.header: #000080"
}
},
{
"c": "@@",
"t": "source.diff meta.diff.range.unified punctuation.definition.range.diff",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": " ",
"t": "source.diff meta.diff.range.unified",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "-1,7 +1,6",
"t": "source.diff meta.diff.range.unified meta.toc-list.line-number.diff",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": " ",
"t": "source.diff meta.diff.range.unified",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "@@",
"t": "source.diff meta.diff.range.unified punctuation.definition.range.diff",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "-",
"t": "source.diff markup.deleted.diff punctuation.definition.deleted.diff",
"r": {
"dark_plus": "markup.deleted: #CE9178",
"light_plus": "markup.deleted: #A31515",
"dark_vs": "markup.deleted: #CE9178",
"light_vs": "markup.deleted: #A31515",
"hc_black": "markup.deleted: #CE9178"
}
},
{
"c": "The Way that can be told of is not the eternal Way;",
"t": "source.diff markup.deleted.diff",
"r": {
"dark_plus": "markup.deleted: #CE9178",
"light_plus": "markup.deleted: #A31515",
"dark_vs": "markup.deleted: #CE9178",
"light_vs": "markup.deleted: #A31515",
"hc_black": "markup.deleted: #CE9178"
}
},
{
"c": "-",
"t": "source.diff markup.deleted.diff punctuation.definition.deleted.diff",
"r": {
"dark_plus": "markup.deleted: #CE9178",
"light_plus": "markup.deleted: #A31515",
"dark_vs": "markup.deleted: #CE9178",
"light_vs": "markup.deleted: #A31515",
"hc_black": "markup.deleted: #CE9178"
}
},
{
"c": "The name that can be named is not the eternal name.",
"t": "source.diff markup.deleted.diff",
"r": {
"dark_plus": "markup.deleted: #CE9178",
"light_plus": "markup.deleted: #A31515",
"dark_vs": "markup.deleted: #CE9178",
"light_vs": "markup.deleted: #A31515",
"hc_black": "markup.deleted: #CE9178"
}
},
{
"c": " The Nameless is the origin of Heaven and Earth;",
"t": "source.diff",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "-",
"t": "source.diff markup.deleted.diff punctuation.definition.deleted.diff",
"r": {
"dark_plus": "markup.deleted: #CE9178",
"light_plus": "markup.deleted: #A31515",
"dark_vs": "markup.deleted: #CE9178",
"light_vs": "markup.deleted: #A31515",
"hc_black": "markup.deleted: #CE9178"
}
},
{
"c": "The Named is the mother of all things.",
"t": "source.diff markup.deleted.diff",
"r": {
"dark_plus": "markup.deleted: #CE9178",
"light_plus": "markup.deleted: #A31515",
"dark_vs": "markup.deleted: #CE9178",
"light_vs": "markup.deleted: #A31515",
"hc_black": "markup.deleted: #CE9178"
}
},
{
"c": "+",
"t": "source.diff markup.inserted.diff punctuation.definition.inserted.diff",
"r": {
"dark_plus": "markup.inserted: #B5CEA8",
"light_plus": "markup.inserted: #09885A",
"dark_vs": "markup.inserted: #B5CEA8",
"light_vs": "markup.inserted: #09885A",
"hc_black": "markup.inserted: #B5CEA8"
}
},
{
"c": "The named is the mother of all things.",
"t": "source.diff markup.inserted.diff",
"r": {
"dark_plus": "markup.inserted: #B5CEA8",
"light_plus": "markup.inserted: #09885A",
"dark_vs": "markup.inserted: #B5CEA8",
"light_vs": "markup.inserted: #09885A",
"hc_black": "markup.inserted: #B5CEA8"
}
},
{
"c": "+",
"t": "source.diff markup.inserted.diff punctuation.definition.inserted.diff",
"r": {
"dark_plus": "markup.inserted: #B5CEA8",
"light_plus": "markup.inserted: #09885A",
"dark_vs": "markup.inserted: #B5CEA8",
"light_vs": "markup.inserted: #09885A",
"hc_black": "markup.inserted: #B5CEA8"
}
},
{
"c": " Therefore let there always be non-being,",
"t": "source.diff",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": " so we may see their subtlety,",
"t": "source.diff",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": " And let there always be being,",
"t": "source.diff",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "@@",
"t": "source.diff meta.diff.range.unified punctuation.definition.range.diff",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": " ",
"t": "source.diff meta.diff.range.unified",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "-9,3 +8,6",
"t": "source.diff meta.diff.range.unified meta.toc-list.line-number.diff",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": " ",
"t": "source.diff meta.diff.range.unified",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "@@",
"t": "source.diff meta.diff.range.unified punctuation.definition.range.diff",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": " The two are the same,",
"t": "source.diff",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": " But after they are produced,",
"t": "source.diff",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": " they have different names.",
"t": "source.diff",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "+",
"t": "source.diff markup.inserted.diff punctuation.definition.inserted.diff",
"r": {
"dark_plus": "markup.inserted: #B5CEA8",
"light_plus": "markup.inserted: #09885A",
"dark_vs": "markup.inserted: #B5CEA8",
"light_vs": "markup.inserted: #09885A",
"hc_black": "markup.inserted: #B5CEA8"
}
},
{
"c": "They both may be called deep and profound.",
"t": "source.diff markup.inserted.diff",
"r": {
"dark_plus": "markup.inserted: #B5CEA8",
"light_plus": "markup.inserted: #09885A",
"dark_vs": "markup.inserted: #B5CEA8",
"light_vs": "markup.inserted: #09885A",
"hc_black": "markup.inserted: #B5CEA8"
}
},
{
"c": "+",
"t": "source.diff markup.inserted.diff punctuation.definition.inserted.diff",
"r": {
"dark_plus": "markup.inserted: #B5CEA8",
"light_plus": "markup.inserted: #09885A",
"dark_vs": "markup.inserted: #B5CEA8",
"light_vs": "markup.inserted: #09885A",
"hc_black": "markup.inserted: #B5CEA8"
}
},
{
"c": "Deeper and more profound,",
"t": "source.diff markup.inserted.diff",
"r": {
"dark_plus": "markup.inserted: #B5CEA8",
"light_plus": "markup.inserted: #09885A",
"dark_vs": "markup.inserted: #B5CEA8",
"light_vs": "markup.inserted: #09885A",
"hc_black": "markup.inserted: #B5CEA8"
}
},
{
"c": "+",
"t": "source.diff markup.inserted.diff punctuation.definition.inserted.diff",
"r": {
"dark_plus": "markup.inserted: #B5CEA8",
"light_plus": "markup.inserted: #09885A",
"dark_vs": "markup.inserted: #B5CEA8",
"light_vs": "markup.inserted: #09885A",
"hc_black": "markup.inserted: #B5CEA8"
}
},
{
"c": "The door of all subtleties!",
"t": "source.diff markup.inserted.diff",
"r": {
"dark_plus": "markup.inserted: #B5CEA8",
"light_plus": "markup.inserted: #09885A",
"dark_vs": "markup.inserted: #B5CEA8",
"light_vs": "markup.inserted: #09885A",
"hc_black": "markup.inserted: #B5CEA8"
}
}
]

View File

@@ -0,0 +1 @@
test/**

View File

@@ -0,0 +1,8 @@
// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS:
[{
"name": "language-docker",
"version": "0.0.0",
"license": "Apache2",
"repositoryURL": "https://github.com/moby/moby",
"description": "The file syntaxes/Dockerfile.tmLanguage was included from https://github.com/moby/moby/blob/master/contrib/syntax/textmate/Docker.tmbundle/Syntaxes/Dockerfile.tmLanguage."
}]

View File

@@ -0,0 +1,24 @@
{
"comments": {
"lineComment": "#"
},
"brackets": [
["{", "}"],
["[", "]"],
["(", ")"]
],
"autoClosingPairs": [
["{", "}"],
["[", "]"],
["(", ")"],
["\"", "\""],
["'", "'"]
],
"surroundingPairs": [
["{", "}"],
["[", "]"],
["(", ")"],
["\"", "\""],
["'", "'"]
]
}

View File

@@ -0,0 +1,23 @@
{
"name": "docker",
"version": "0.1.0",
"publisher": "vscode",
"engines": { "vscode": "*" },
"scripts": {
"update-grammar": "node ../../build/npm/update-grammar.js moby/moby contrib/syntax/textmate/Docker.tmbundle/Syntaxes/Dockerfile.tmLanguage ./syntaxes/docker.tmLanguage.json"
},
"contributes": {
"languages": [{
"id": "dockerfile",
"extensions": [ ".dockerfile" ],
"filenames": [ "Dockerfile" ],
"aliases": [ "Dockerfile" ],
"configuration": "./language-configuration.json"
}],
"grammars": [{
"language": "dockerfile",
"scopeName": "source.dockerfile",
"path": "./syntaxes/docker.tmLanguage.json"
}]
}
}

View File

@@ -0,0 +1,106 @@
{
"information_for_contributors": [
"This file has been converted from https://github.com/moby/moby/blob/master/contrib/syntax/textmate/Docker.tmbundle/Syntaxes/Dockerfile.tmLanguage",
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
"Once accepted there, we are happy to receive an update request."
],
"version": "https://github.com/moby/moby/commit/abd39744c6f3ed854500e423f5fabf952165161f",
"fileTypes": [
"Dockerfile"
],
"name": "Dockerfile",
"patterns": [
{
"captures": {
"1": {
"name": "keyword.other.special-method.dockerfile"
},
"2": {
"name": "keyword.other.special-method.dockerfile"
}
},
"match": "^\\s*\\b(?i:(FROM))\\b.*?\\b(?i:(AS))\\b"
},
{
"captures": {
"1": {
"name": "keyword.control.dockerfile"
},
"2": {
"name": "keyword.other.special-method.dockerfile"
}
},
"match": "^\\s*(?i:(ONBUILD)\\s+)?(?i:(ADD|ARG|CMD|COPY|ENTRYPOINT|ENV|EXPOSE|FROM|HEALTHCHECK|LABEL|MAINTAINER|RUN|SHELL|STOPSIGNAL|USER|VOLUME|WORKDIR))\\s"
},
{
"captures": {
"1": {
"name": "keyword.operator.dockerfile"
},
"2": {
"name": "keyword.other.special-method.dockerfile"
}
},
"match": "^\\s*(?i:(ONBUILD)\\s+)?(?i:(CMD|ENTRYPOINT))\\s"
},
{
"begin": "\"",
"beginCaptures": {
"1": {
"name": "punctuation.definition.string.begin.dockerfile"
}
},
"end": "\"",
"endCaptures": {
"1": {
"name": "punctuation.definition.string.end.dockerfile"
}
},
"name": "string.quoted.double.dockerfile",
"patterns": [
{
"match": "\\\\.",
"name": "constant.character.escaped.dockerfile"
}
]
},
{
"begin": "'",
"beginCaptures": {
"1": {
"name": "punctuation.definition.string.begin.dockerfile"
}
},
"end": "'",
"endCaptures": {
"1": {
"name": "punctuation.definition.string.end.dockerfile"
}
},
"name": "string.quoted.single.dockerfile",
"patterns": [
{
"match": "\\\\.",
"name": "constant.character.escaped.dockerfile"
}
]
},
{
"captures": {
"1": {
"name": "punctuation.whitespace.comment.leading.dockerfile"
},
"2": {
"name": "comment.line.number-sign.dockerfile"
},
"3": {
"name": "punctuation.definition.comment.dockerfile"
}
},
"comment": "comment.line",
"match": "^(\\s*)((#).*$\\n?)"
}
],
"scopeName": "source.dockerfile",
"uuid": "a39d8795-59d2-49af-aa00-fe74ee29576e"
}

View File

@@ -0,0 +1,14 @@
FROM ubuntu
MAINTAINER Kimbro Staken
RUN apt-get install -y software-properties-common python
RUN add-apt-repository ppa:chris-lea/node.js
RUN echo "deb http://us.archive.ubuntu.com/ubuntu/ precise universe" >> /etc/apt/sources.list
RUN apt-get update
RUN apt-get install -y nodejs
#RUN apt-get install -y nodejs=0.6.12~dfsg1-1ubuntu1
RUN mkdir /var/www
ADD app.js /var/www/app.js
CMD ["/usr/bin/node", "/var/www/app.js"]

View File

@@ -0,0 +1,310 @@
[
{
"c": "FROM",
"t": "source.dockerfile keyword.other.special-method.dockerfile",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
"dark_vs": "keyword: #569CD6",
"light_vs": "keyword: #0000FF",
"hc_black": "keyword: #569CD6"
}
},
{
"c": " ubuntu",
"t": "source.dockerfile",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "MAINTAINER",
"t": "source.dockerfile keyword.other.special-method.dockerfile",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
"dark_vs": "keyword: #569CD6",
"light_vs": "keyword: #0000FF",
"hc_black": "keyword: #569CD6"
}
},
{
"c": " Kimbro Staken",
"t": "source.dockerfile",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "RUN",
"t": "source.dockerfile keyword.other.special-method.dockerfile",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
"dark_vs": "keyword: #569CD6",
"light_vs": "keyword: #0000FF",
"hc_black": "keyword: #569CD6"
}
},
{
"c": " apt-get install -y software-properties-common python",
"t": "source.dockerfile",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "RUN",
"t": "source.dockerfile keyword.other.special-method.dockerfile",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
"dark_vs": "keyword: #569CD6",
"light_vs": "keyword: #0000FF",
"hc_black": "keyword: #569CD6"
}
},
{
"c": " add-apt-repository ppa:chris-lea/node.js",
"t": "source.dockerfile",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "RUN",
"t": "source.dockerfile keyword.other.special-method.dockerfile",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
"dark_vs": "keyword: #569CD6",
"light_vs": "keyword: #0000FF",
"hc_black": "keyword: #569CD6"
}
},
{
"c": " echo ",
"t": "source.dockerfile",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "\"deb http://us.archive.ubuntu.com/ubuntu/ precise universe\"",
"t": "source.dockerfile string.quoted.double.dockerfile",
"r": {
"dark_plus": "string: #CE9178",
"light_plus": "string: #A31515",
"dark_vs": "string: #CE9178",
"light_vs": "string: #A31515",
"hc_black": "string: #CE9178"
}
},
{
"c": " >> /etc/apt/sources.list",
"t": "source.dockerfile",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "RUN",
"t": "source.dockerfile keyword.other.special-method.dockerfile",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
"dark_vs": "keyword: #569CD6",
"light_vs": "keyword: #0000FF",
"hc_black": "keyword: #569CD6"
}
},
{
"c": " apt-get update",
"t": "source.dockerfile",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "RUN",
"t": "source.dockerfile keyword.other.special-method.dockerfile",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
"dark_vs": "keyword: #569CD6",
"light_vs": "keyword: #0000FF",
"hc_black": "keyword: #569CD6"
}
},
{
"c": " apt-get install -y nodejs",
"t": "source.dockerfile",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "#",
"t": "source.dockerfile comment.line.number-sign.dockerfile punctuation.definition.comment.dockerfile",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
"dark_vs": "comment: #608B4E",
"light_vs": "comment: #008000",
"hc_black": "comment: #7CA668"
}
},
{
"c": "RUN apt-get install -y nodejs=0.6.12~dfsg1-1ubuntu1",
"t": "source.dockerfile comment.line.number-sign.dockerfile",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
"dark_vs": "comment: #608B4E",
"light_vs": "comment: #008000",
"hc_black": "comment: #7CA668"
}
},
{
"c": "RUN",
"t": "source.dockerfile keyword.other.special-method.dockerfile",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
"dark_vs": "keyword: #569CD6",
"light_vs": "keyword: #0000FF",
"hc_black": "keyword: #569CD6"
}
},
{
"c": " mkdir /var/www",
"t": "source.dockerfile",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "ADD",
"t": "source.dockerfile keyword.other.special-method.dockerfile",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
"dark_vs": "keyword: #569CD6",
"light_vs": "keyword: #0000FF",
"hc_black": "keyword: #569CD6"
}
},
{
"c": " app.js /var/www/app.js",
"t": "source.dockerfile",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "CMD",
"t": "source.dockerfile keyword.other.special-method.dockerfile",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
"dark_vs": "keyword: #569CD6",
"light_vs": "keyword: #0000FF",
"hc_black": "keyword: #569CD6"
}
},
{
"c": " [",
"t": "source.dockerfile",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "\"/usr/bin/node\"",
"t": "source.dockerfile string.quoted.double.dockerfile",
"r": {
"dark_plus": "string: #CE9178",
"light_plus": "string: #A31515",
"dark_vs": "string: #CE9178",
"light_vs": "string: #A31515",
"hc_black": "string: #CE9178"
}
},
{
"c": ", ",
"t": "source.dockerfile",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "\"/var/www/app.js\"",
"t": "source.dockerfile string.quoted.double.dockerfile",
"r": {
"dark_plus": "string: #CE9178",
"light_plus": "string: #A31515",
"dark_vs": "string: #CE9178",
"light_vs": "string: #A31515",
"hc_black": "string: #CE9178"
}
},
{
"c": "] ",
"t": "source.dockerfile",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
}
]

View File

@@ -0,0 +1,4 @@
test/**
src/**
tsconfig.json
npm-shrinkwrap.json

View File

@@ -0,0 +1,61 @@
{
"name": "extension-editing",
"version": "0.0.1",
"dependencies": {
"@types/node": {
"version": "6.0.78",
"from": "@types/node@>=6.0.46 <7.0.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.78.tgz"
},
"argparse": {
"version": "1.0.9",
"from": "argparse@>=1.0.7 <2.0.0",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz"
},
"entities": {
"version": "1.1.1",
"from": "entities@>=1.1.1 <1.2.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz"
},
"jsonc-parser": {
"version": "0.3.1",
"from": "jsonc-parser@>=0.3.1 <0.4.0",
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-0.3.1.tgz"
},
"linkify-it": {
"version": "2.0.3",
"from": "linkify-it@>=2.0.0 <3.0.0",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.0.3.tgz"
},
"markdown-it": {
"version": "8.3.1",
"from": "markdown-it@>=8.3.1 <9.0.0",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.3.1.tgz"
},
"mdurl": {
"version": "1.0.1",
"from": "mdurl@>=1.0.1 <2.0.0",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz"
},
"parse5": {
"version": "3.0.2",
"from": "parse5@>=3.0.2 <4.0.0",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.2.tgz"
},
"sprintf-js": {
"version": "1.0.3",
"from": "sprintf-js@>=1.0.2 <1.1.0",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz"
},
"uc.micro": {
"version": "1.0.3",
"from": "uc.micro@>=1.0.3 <2.0.0",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.3.tgz"
},
"vscode-nls": {
"version": "2.0.2",
"from": "vscode-nls@>=2.0.1 <3.0.0",
"resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz"
}
}
}

View File

@@ -0,0 +1,52 @@
{
"name": "extension-editing",
"version": "0.0.1",
"publisher": "vscode",
"engines": {
"vscode": "^1.4.0"
},
"categories": [
"Languages",
"Other"
],
"activationEvents": [
"onLanguage:json",
"onLanguage:markdown",
"onLanguage:typescript"
],
"main": "./out/extension",
"scripts": {
"compile": "gulp compile-extension:extension-editing",
"watch": "gulp watch-extension:extension-editing"
},
"dependencies": {
"jsonc-parser": "^0.3.1",
"markdown-it": "^8.3.1",
"parse5": "^3.0.2",
"vscode-nls": "^2.0.1"
},
"contributes": {
"jsonValidation": [
{
"fileMatch": "package.json",
"url": "vscode://schemas/vscode-extensions"
},
{
"fileMatch": "*language-configuration.json",
"url": "vscode://schemas/language-configuration"
},
{
"fileMatch": "*icon-theme.json",
"url": "vscode://schemas/icon-theme"
},
{
"fileMatch": "*color-theme.json",
"url": "vscode://schemas/color-theme"
}
]
},
"devDependencies": {
"@types/markdown-it": "0.0.2",
"@types/node": "^7.0.4"
}
}

View File

@@ -0,0 +1,120 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as vscode from 'vscode';
import * as ts from 'typescript';
import { PackageDocument } from './packageDocumentHelper';
import { ExtensionLinter } from './extensionLinter';
export function activate(context: vscode.ExtensionContext) {
const registration = vscode.languages.registerDocumentLinkProvider({ language: 'typescript', pattern: '**/vscode.d.ts' }, _linkProvider);
context.subscriptions.push(registration);
//package.json suggestions
context.subscriptions.push(registerPackageDocumentCompletions());
context.subscriptions.push(new ExtensionLinter(context));
}
const _linkProvider = new class implements vscode.DocumentLinkProvider {
private _cachedResult: { key: string; links: vscode.DocumentLink[] };
private _linkPattern = /[^!]\[.*?\]\(#(.*?)\)/g;
async provideDocumentLinks(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<vscode.DocumentLink[]> {
const key = `${document.uri.toString()}@${document.version}`;
if (!this._cachedResult || this._cachedResult.key !== key) {
const links = await this._computeDocumentLinks(document);
this._cachedResult = { key, links };
}
return this._cachedResult.links;
}
private async _computeDocumentLinks(document: vscode.TextDocument): Promise<vscode.DocumentLink[]> {
const results: vscode.DocumentLink[] = [];
const text = document.getText();
const lookUp = await ast.createNamedNodeLookUp(text);
this._linkPattern.lastIndex = 0;
let match: RegExpMatchArray;
while ((match = this._linkPattern.exec(text))) {
const offset = lookUp(match[1]);
if (offset === -1) {
console.warn(match[1]);
continue;
}
const targetPos = document.positionAt(offset);
const linkEnd = document.positionAt(this._linkPattern.lastIndex - 1);
const linkStart = linkEnd.translate({ characterDelta: -(1 + match[1].length) });
results.push(new vscode.DocumentLink(
new vscode.Range(linkStart, linkEnd),
document.uri.with({ fragment: `${1 + targetPos.line}` })));
}
return results;
}
};
namespace ast {
export interface NamedNodeLookUp {
(dottedName: string): number;
}
export async function createNamedNodeLookUp(str: string): Promise<NamedNodeLookUp> {
const ts = await import('typescript');
const sourceFile = ts.createSourceFile('fake.d.ts', str, ts.ScriptTarget.Latest);
const identifiers: string[] = [];
const spans: number[] = [];
ts.forEachChild(sourceFile, function visit(node: ts.Node) {
const declIdent = (<ts.NamedDeclaration>node).name;
if (declIdent && declIdent.kind === ts.SyntaxKind.Identifier) {
identifiers.push((<ts.Identifier>declIdent).text);
spans.push(node.pos, node.end);
}
ts.forEachChild(node, visit);
});
return function (dottedName: string): number {
let start = -1;
let end = Number.MAX_VALUE;
for (let name of dottedName.split('.')) {
let idx: number = -1;
while ((idx = identifiers.indexOf(name, idx + 1)) >= 0) {
let myStart = spans[2 * idx];
let myEnd = spans[2 * idx + 1];
if (myStart >= start && myEnd <= end) {
start = myStart;
end = myEnd;
break;
}
}
if (idx < 0) {
return -1;
}
}
return start;
};
}
}
function registerPackageDocumentCompletions(): vscode.Disposable {
return vscode.languages.registerCompletionItemProvider({ language: 'json', pattern: '**/package.json' }, {
provideCompletionItems(document, position, token) {
return new PackageDocument(document).provideCompletionItems(position, token);
}
});
}

View File

@@ -0,0 +1,361 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as fs from 'fs';
import * as path from 'path';
import { parseTree, findNodeAtLocation, Node as JsonNode } from 'jsonc-parser';
import * as nls from 'vscode-nls';
import * as MarkdownIt from 'markdown-it';
import * as parse5 from 'parse5';
import { languages, workspace, Disposable, ExtensionContext, TextDocument, Uri, Diagnostic, Range, DiagnosticSeverity, Position } from 'vscode';
const product = require('../../../product.json');
const allowedBadgeProviders: string[] = (product.extensionAllowedBadgeProviders || []).map(s => s.toLowerCase());
const localize = nls.loadMessageBundle();
const httpsRequired = localize('httpsRequired', "Images must use the HTTPS protocol.");
const svgsNotValid = localize('svgsNotValid', "SVGs are not a valid image source.");
const embeddedSvgsNotValid = localize('embeddedSvgsNotValid', "Embedded SVGs are not a valid image source.");
const dataUrlsNotValid = localize('dataUrlsNotValid', "Data URLs are not a valid image source.");
const relativeUrlRequiresHttpsRepository = localize('relativeUrlRequiresHttpsRepository', "Relative image URLs require a repository with HTTPS protocol to be specified in the package.json.");
const relativeIconUrlRequiresHttpsRepository = localize('relativeIconUrlRequiresHttpsRepository', "An icon requires a repository with HTTPS protocol to be specified in this package.json.");
const relativeBadgeUrlRequiresHttpsRepository = localize('relativeBadgeUrlRequiresHttpsRepository', "Relative badge URLs require a repository with HTTPS protocol to be specified in this package.json.");
enum Context {
ICON,
BADGE,
MARKDOWN
}
interface TokenAndPosition {
token: MarkdownIt.Token;
begin: number;
end: number;
}
interface PackageJsonInfo {
isExtension: boolean;
hasHttpsRepository: boolean;
}
export class ExtensionLinter {
private diagnosticsCollection = languages.createDiagnosticCollection('extension-editing');
private fileWatcher = workspace.createFileSystemWatcher('**/package.json');
private disposables: Disposable[] = [this.diagnosticsCollection, this.fileWatcher];
private folderToPackageJsonInfo: Record<string, PackageJsonInfo> = {};
private packageJsonQ = new Set<TextDocument>();
private readmeQ = new Set<TextDocument>();
private timer: NodeJS.Timer;
private markdownIt = new MarkdownIt();
constructor(private context: ExtensionContext) {
this.disposables.push(
workspace.onDidOpenTextDocument(document => this.queue(document)),
workspace.onDidChangeTextDocument(event => this.queue(event.document)),
workspace.onDidCloseTextDocument(document => this.clear(document)),
this.fileWatcher.onDidChange(uri => this.packageJsonChanged(this.getUriFolder(uri))),
this.fileWatcher.onDidCreate(uri => this.packageJsonChanged(this.getUriFolder(uri))),
this.fileWatcher.onDidDelete(uri => this.packageJsonChanged(this.getUriFolder(uri))),
);
workspace.textDocuments.forEach(document => this.queue(document));
}
private queue(document: TextDocument) {
const p = document.uri.path;
if (document.languageId === 'json' && endsWith(p, '/package.json')) {
this.packageJsonQ.add(document);
this.startTimer();
}
this.queueReadme(document);
}
private queueReadme(document: TextDocument) {
const p = document.uri.path;
if (document.languageId === 'markdown' && (endsWith(p.toLowerCase(), '/readme.md') || endsWith(p.toLowerCase(), '/changelog.md'))) {
this.readmeQ.add(document);
this.startTimer();
}
}
private startTimer() {
if (this.timer) {
clearTimeout(this.timer);
}
this.timer = setTimeout(() => {
this.lint()
.catch(console.error);
}, 300);
}
private async lint() {
this.lintPackageJson();
await this.lintReadme();
}
private lintPackageJson() {
this.packageJsonQ.forEach(document => {
this.packageJsonQ.delete(document);
if (document.isClosed) {
return;
}
const diagnostics: Diagnostic[] = [];
const tree = parseTree(document.getText());
const info = this.readPackageJsonInfo(this.getUriFolder(document.uri), tree);
if (info.isExtension) {
const icon = findNodeAtLocation(tree, ['icon']);
if (icon && icon.type === 'string') {
this.addDiagnostics(diagnostics, document, icon.offset + 1, icon.offset + icon.length - 1, icon.value, Context.ICON, info);
}
const badges = findNodeAtLocation(tree, ['badges']);
if (badges && badges.type === 'array') {
badges.children.map(child => findNodeAtLocation(child, ['url']))
.filter(url => url && url.type === 'string')
.map(url => this.addDiagnostics(diagnostics, document, url.offset + 1, url.offset + url.length - 1, url.value, Context.BADGE, info));
}
}
this.diagnosticsCollection.set(document.uri, diagnostics);
});
}
private async lintReadme() {
for (const document of Array.from(this.readmeQ)) {
this.readmeQ.delete(document);
if (document.isClosed) {
return;
}
const folder = this.getUriFolder(document.uri);
let info = this.folderToPackageJsonInfo[folder.toString()];
if (!info) {
const tree = await this.loadPackageJson(folder);
info = this.readPackageJsonInfo(folder, tree);
}
if (!info.isExtension) {
this.diagnosticsCollection.set(document.uri, []);
return;
}
const text = document.getText();
const tokens = this.markdownIt.parse(text, {});
const tokensAndPositions = (function toTokensAndPositions(this: ExtensionLinter, tokens: MarkdownIt.Token[], begin = 0, end = text.length): TokenAndPosition[] {
const tokensAndPositions = tokens.map<TokenAndPosition>(token => {
if (token.map) {
const tokenBegin = document.offsetAt(new Position(token.map[0], 0));
const tokenEnd = begin = document.offsetAt(new Position(token.map[1], 0));
return {
token,
begin: tokenBegin,
end: tokenEnd
};
}
const image = token.type === 'image' && this.locateToken(text, begin, end, token, token.attrGet('src'));
const other = image || this.locateToken(text, begin, end, token, token.content);
return other || {
token,
begin,
end: begin
};
});
return tokensAndPositions.concat(
...tokensAndPositions.filter(tnp => tnp.token.children && tnp.token.children.length)
.map(tnp => toTokensAndPositions.call(this, tnp.token.children, tnp.begin, tnp.end))
);
}).call(this, tokens);
const diagnostics: Diagnostic[] = [];
tokensAndPositions.filter(tnp => tnp.token.type === 'image' && tnp.token.attrGet('src'))
.map(inp => {
const src = inp.token.attrGet('src');
const begin = text.indexOf(src, inp.begin);
if (begin !== -1 && begin < inp.end) {
this.addDiagnostics(diagnostics, document, begin, begin + src.length, src, Context.MARKDOWN, info);
} else {
const content = inp.token.content;
const begin = text.indexOf(content, inp.begin);
if (begin !== -1 && begin < inp.end) {
this.addDiagnostics(diagnostics, document, begin, begin + content.length, src, Context.MARKDOWN, info);
}
}
});
let svgStart: Diagnostic;
tokensAndPositions.filter(tnp => tnp.token.type === 'text' && tnp.token.content)
.map(tnp => {
const parser = new parse5.SAXParser({ locationInfo: true });
parser.on('startTag', (name, attrs, selfClosing, location) => {
if (name === 'img') {
const src = attrs.find(a => a.name === 'src');
if (src && src.value) {
const begin = text.indexOf(src.value, tnp.begin + location.startOffset);
if (begin !== -1 && begin < tnp.end) {
this.addDiagnostics(diagnostics, document, begin, begin + src.value.length, src.value, Context.MARKDOWN, info);
}
}
} else if (name === 'svg') {
const begin = tnp.begin + location.startOffset;
const end = tnp.begin + location.endOffset;
const range = new Range(document.positionAt(begin), document.positionAt(end));
svgStart = new Diagnostic(range, embeddedSvgsNotValid, DiagnosticSeverity.Warning);
diagnostics.push(svgStart);
}
});
parser.on('endTag', (name, location) => {
if (name === 'svg' && svgStart) {
const end = tnp.begin + location.endOffset;
svgStart.range = new Range(svgStart.range.start, document.positionAt(end));
}
});
parser.write(tnp.token.content);
parser.end();
});
this.diagnosticsCollection.set(document.uri, diagnostics);
};
}
private locateToken(text: string, begin: number, end: number, token: MarkdownIt.Token, content: string) {
if (content) {
const tokenBegin = text.indexOf(content, begin);
if (tokenBegin !== -1) {
const tokenEnd = tokenBegin + content.length;
if (tokenEnd <= end) {
begin = tokenEnd;
return {
token,
begin: tokenBegin,
end: tokenEnd
};
}
}
}
}
private readPackageJsonInfo(folder: Uri, tree: JsonNode) {
const engine = tree && findNodeAtLocation(tree, ['engines', 'vscode']);
const repo = tree && findNodeAtLocation(tree, ['repository', 'url']);
const info: PackageJsonInfo = {
isExtension: !!(engine && engine.type === 'string'),
hasHttpsRepository: !!(repo && repo.type === 'string' && repo.value && parseUri(repo.value).scheme.toLowerCase() === 'https')
};
const str = folder.toString();
const oldInfo = this.folderToPackageJsonInfo[str];
if (oldInfo && (oldInfo.isExtension !== info.isExtension || oldInfo.hasHttpsRepository !== info.hasHttpsRepository)) {
this.packageJsonChanged(folder); // clears this.folderToPackageJsonInfo[str]
}
this.folderToPackageJsonInfo[str] = info;
return info;
}
private async loadPackageJson(folder: Uri) {
const file = folder.with({ path: path.posix.join(folder.path, 'package.json') });
const exists = await fileExists(file.fsPath);
if (!exists) {
return undefined;
}
const document = await workspace.openTextDocument(file);
return parseTree(document.getText());
}
private packageJsonChanged(folder: Uri) {
delete this.folderToPackageJsonInfo[folder.toString()];
const str = folder.toString().toLowerCase();
workspace.textDocuments.filter(document => this.getUriFolder(document.uri).toString().toLowerCase() === str)
.forEach(document => this.queueReadme(document));
}
private getUriFolder(uri: Uri) {
return uri.with({ path: path.posix.dirname(uri.path) });
}
private addDiagnostics(diagnostics: Diagnostic[], document: TextDocument, begin: number, end: number, src: string, context: Context, info: PackageJsonInfo) {
const uri = parseUri(src);
const scheme = uri.scheme.toLowerCase();
if (scheme && scheme !== 'https' && scheme !== 'data') {
const range = new Range(document.positionAt(begin), document.positionAt(end));
diagnostics.push(new Diagnostic(range, httpsRequired, DiagnosticSeverity.Warning));
}
if (scheme === 'data') {
const range = new Range(document.positionAt(begin), document.positionAt(end));
diagnostics.push(new Diagnostic(range, dataUrlsNotValid, DiagnosticSeverity.Warning));
}
if (!scheme && !info.hasHttpsRepository) {
const range = new Range(document.positionAt(begin), document.positionAt(end));
let message = (() => {
switch (context) {
case Context.ICON: return relativeIconUrlRequiresHttpsRepository;
case Context.BADGE: return relativeBadgeUrlRequiresHttpsRepository;
default: return relativeUrlRequiresHttpsRepository;
}
})();
diagnostics.push(new Diagnostic(range, message, DiagnosticSeverity.Warning));
}
if (endsWith(uri.path.toLowerCase(), '.svg') && allowedBadgeProviders.indexOf(uri.authority.toLowerCase()) === -1) {
const range = new Range(document.positionAt(begin), document.positionAt(end));
diagnostics.push(new Diagnostic(range, svgsNotValid, DiagnosticSeverity.Warning));
}
}
private clear(document: TextDocument) {
this.diagnosticsCollection.delete(document.uri);
this.packageJsonQ.delete(document);
}
public dispose() {
this.disposables.forEach(d => d.dispose());
this.disposables = [];
}
}
function endsWith(haystack: string, needle: string): boolean {
let diff = haystack.length - needle.length;
if (diff > 0) {
return haystack.indexOf(needle, diff) === diff;
} else if (diff === 0) {
return haystack === needle;
} else {
return false;
}
}
function fileExists(path: string): Promise<boolean> {
return new Promise((resolve, reject) => {
fs.lstat(path, (err, stats) => {
if (!err) {
resolve(true);
} else if (err.code === 'ENOENT') {
resolve(false);
} else {
reject(err);
}
});
});
}
function parseUri(src: string) {
try {
return Uri.parse(src);
} catch (err) {
try {
return Uri.parse(encodeURI(src));
} catch (err) {
return Uri.parse('');
}
}
}

View File

@@ -0,0 +1,84 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { getLocation, Location } from 'jsonc-parser';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
export class PackageDocument {
constructor(private document: vscode.TextDocument) { }
public provideCompletionItems(position: vscode.Position, token: vscode.CancellationToken): vscode.ProviderResult<vscode.CompletionItem[]> {
const location = getLocation(this.document.getText(), this.document.offsetAt(position));
if (location.path.length >= 2 && location.path[1] === 'configurationDefaults') {
return this.provideLanguageOverridesCompletionItems(location, position);
}
}
private provideLanguageOverridesCompletionItems(location: Location, position: vscode.Position): vscode.ProviderResult<vscode.CompletionItem[]> {
let range = this.document.getWordRangeAtPosition(position) || new vscode.Range(position, position);
const text = this.document.getText(range);
if (location.path.length === 2) {
let snippet = '"[${1:language}]": {\n\t"$0"\n}';
// Suggestion model word matching includes quotes,
// hence exclude the starting quote from the snippet and the range
// ending quote gets replaced
if (text && text.startsWith('"')) {
range = new vscode.Range(new vscode.Position(range.start.line, range.start.character + 1), range.end);
snippet = snippet.substring(1);
}
return Promise.resolve([this.newSnippetCompletionItem({
label: localize('languageSpecificEditorSettings', "Language specific editor settings"),
documentation: localize('languageSpecificEditorSettingsDescription', "Override editor settings for language"),
snippet,
range
})]);
}
if (location.path.length === 3 && location.previousNode && typeof location.previousNode.value === 'string' && location.previousNode.value.startsWith('[')) {
// Suggestion model word matching includes starting quote and open sqaure bracket
// Hence exclude them from the proposal range
range = new vscode.Range(new vscode.Position(range.start.line, range.start.character + 2), range.end);
return vscode.languages.getLanguages().then(languages => {
return languages.map(l => {
// Suggestion model word matching includes closed sqaure bracket and ending quote
// Hence include them in the proposal to replace
return this.newSimpleCompletionItem(l, range, '', l + ']"');
});
});
}
return Promise.resolve([]);
}
private newSimpleCompletionItem(text: string, range: vscode.Range, description?: string, insertText?: string): vscode.CompletionItem {
const item = new vscode.CompletionItem(text);
item.kind = vscode.CompletionItemKind.Value;
item.detail = description;
item.insertText = insertText ? insertText : text;
item.range = range;
return item;
}
private newSnippetCompletionItem(o: { label: string; documentation?: string; snippet: string; range: vscode.Range; }): vscode.CompletionItem {
const item = new vscode.CompletionItem(o.label);
item.kind = vscode.CompletionItemKind.Value;
item.documentation = o.documentation;
item.insertText = new vscode.SnippetString(o.snippet);
item.range = o.range;
return item;
}
}

View File

@@ -0,0 +1,6 @@
/*---------------------------------------------------------------------------------------------
* 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'/>

View File

@@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"es2015"
],
"module": "commonjs",
"outDir": "./out"
},
"include": [
"src/**/*"
]
}

View File

@@ -0,0 +1,5 @@
src/**
test/**
out/test/**
tsconfig.json
npm-shrinkwrap.json

View File

@@ -0,0 +1,29 @@
// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS:
[{
"name": "textmate/git.tmbundle",
"version": "0.0.0",
"license": "MIT",
"repositoryURL": "https://github.com/textmate/git.tmbundle",
"licenseDetail": [
"Copyright (c) 2008 Tim Harper",
"",
"Permission is hereby granted, free of charge, to any person obtaining",
"a copy of this software and associated documentation files (the\"",
"Software\"), to deal in the Software without restriction, including",
"without limitation the rights to use, copy, modify, merge, publish,",
"distribute, sublicense, and/or sell copies of the Software, and to",
"permit persons to whom the Software is furnished to do so, subject to",
"the following conditions:",
"",
"The above copyright notice and this permission notice shall be",
"included in all copies or substantial portions of the Software.",
"",
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,",
"EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF",
"MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND",
"NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE",
"LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION",
"OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION",
"WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
]
}]

31
extensions/git/npm-shrinkwrap.json generated Normal file
View File

@@ -0,0 +1,31 @@
{
"name": "git",
"version": "0.0.1",
"dependencies": {
"applicationinsights": {
"version": "0.18.0",
"from": "applicationinsights@0.18.0",
"resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-0.18.0.tgz"
},
"iconv-lite": {
"version": "0.4.15",
"from": "iconv-lite@0.4.15",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz"
},
"vscode-extension-telemetry": {
"version": "0.0.7",
"from": "vscode-extension-telemetry@>=0.0.8 <0.0.9",
"resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.8.tgz"
},
"vscode-nls": {
"version": "2.0.2",
"from": "vscode-nls@>=2.0.1 <3.0.0",
"resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz"
},
"winreg": {
"version": "1.2.3",
"from": "winreg@1.2.3",
"resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.3.tgz"
}
}
}

801
extensions/git/package.json Normal file
View File

@@ -0,0 +1,801 @@
{
"name": "git",
"publisher": "vscode",
"displayName": "git",
"description": "Git",
"version": "0.0.1",
"engines": {
"vscode": "^1.5.0"
},
"aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
"enableProposedApi": true,
"categories": [
"Other"
],
"activationEvents": [
"*"
],
"main": "./out/main",
"scripts": {
"compile": "gulp compile-extension:git",
"watch": "gulp watch-extension:git"
},
"contributes": {
"commands": [
{
"command": "git.clone",
"title": "%command.clone%",
"category": "Git"
},
{
"command": "git.init",
"title": "%command.init%",
"category": "Git",
"icon": {
"light": "resources/icons/light/git.svg",
"dark": "resources/icons/dark/git.svg"
}
},
{
"command": "git.refresh",
"title": "%command.refresh%",
"category": "Git",
"icon": {
"light": "resources/icons/light/refresh.svg",
"dark": "resources/icons/dark/refresh.svg"
}
},
{
"command": "git.openChange",
"title": "%command.openChange%",
"category": "Git",
"icon": {
"light": "resources/icons/light/open-change.svg",
"dark": "resources/icons/dark/open-change.svg"
}
},
{
"command": "git.openFile",
"title": "%command.openFile%",
"category": "Git",
"icon": {
"light": "resources/icons/light/open-file.svg",
"dark": "resources/icons/dark/open-file.svg"
}
},
{
"command": "git.openHEADFile",
"title": "%command.openHEADFile%",
"category": "Git"
},
{
"command": "git.stage",
"title": "%command.stage%",
"category": "Git",
"icon": {
"light": "resources/icons/light/stage.svg",
"dark": "resources/icons/dark/stage.svg"
}
},
{
"command": "git.stageAll",
"title": "%command.stageAll%",
"category": "Git",
"icon": {
"light": "resources/icons/light/stage.svg",
"dark": "resources/icons/dark/stage.svg"
}
},
{
"command": "git.stageSelectedRanges",
"title": "%command.stageSelectedRanges%",
"category": "Git"
},
{
"command": "git.revertSelectedRanges",
"title": "%command.revertSelectedRanges%",
"category": "Git"
},
{
"command": "git.unstage",
"title": "%command.unstage%",
"category": "Git",
"icon": {
"light": "resources/icons/light/unstage.svg",
"dark": "resources/icons/dark/unstage.svg"
}
},
{
"command": "git.unstageAll",
"title": "%command.unstageAll%",
"category": "Git",
"icon": {
"light": "resources/icons/light/unstage.svg",
"dark": "resources/icons/dark/unstage.svg"
}
},
{
"command": "git.unstageSelectedRanges",
"title": "%command.unstageSelectedRanges%",
"category": "Git"
},
{
"command": "git.clean",
"title": "%command.clean%",
"category": "Git",
"icon": {
"light": "resources/icons/light/clean.svg",
"dark": "resources/icons/dark/clean.svg"
}
},
{
"command": "git.cleanAll",
"title": "%command.cleanAll%",
"category": "Git",
"icon": {
"light": "resources/icons/light/clean.svg",
"dark": "resources/icons/dark/clean.svg"
}
},
{
"command": "git.commit",
"title": "%command.commit%",
"category": "Git",
"icon": {
"light": "resources/icons/light/check.svg",
"dark": "resources/icons/dark/check.svg"
}
},
{
"command": "git.commitStaged",
"title": "%command.commitStaged%",
"category": "Git"
},
{
"command": "git.commitStagedSigned",
"title": "%command.commitStagedSigned%",
"category": "Git"
},
{
"command": "git.commitStagedAmend",
"title": "%command.commitStagedAmend%",
"category": "Git"
},
{
"command": "git.commitAll",
"title": "%command.commitAll%",
"category": "Git"
},
{
"command": "git.commitAllSigned",
"title": "%command.commitAllSigned%",
"category": "Git"
},
{
"command": "git.commitAllAmend",
"title": "%command.commitAllAmend%",
"category": "Git"
},
{
"command": "git.undoCommit",
"title": "%command.undoCommit%",
"category": "Git"
},
{
"command": "git.checkout",
"title": "%command.checkout%",
"category": "Git"
},
{
"command": "git.branch",
"title": "%command.branch%",
"category": "Git"
},
{
"command": "git.deleteBranch",
"title": "%command.deleteBranch%",
"category": "Git"
},
{
"command": "git.merge",
"title": "%command.merge%",
"category": "Git"
},
{
"command": "git.createTag",
"title": "%command.createTag%",
"category": "Git"
},
{
"command": "git.pull",
"title": "%command.pull%",
"category": "Git"
},
{
"command": "git.pullRebase",
"title": "%command.pullRebase%",
"category": "Git"
},
{
"command": "git.pullFrom",
"title": "%command.pullFrom%",
"category": "Git"
},
{
"command": "git.push",
"title": "%command.push%",
"category": "Git"
},
{
"command": "git.pushTo",
"title": "%command.pushTo%",
"category": "Git"
},
{
"command": "git.pushWithTags",
"title": "%command.pushWithTags%",
"category": "Git"
},
{
"command": "git.sync",
"title": "%command.sync%",
"category": "Git"
},
{
"command": "git.publish",
"title": "%command.publish%",
"category": "Git"
},
{
"command": "git.showOutput",
"title": "%command.showOutput%",
"category": "Git"
},
{
"command": "git.ignore",
"title": "%command.ignore%",
"category": "Git"
},
{
"command": "git.stash",
"title": "%command.stash%",
"category": "Git"
},
{
"command": "git.stashPop",
"title": "%command.stashPop%",
"category": "Git"
},
{
"command": "git.stashPopLatest",
"title": "%command.stashPopLatest%",
"category": "Git"
}
],
"menus": {
"commandPalette": [
{
"command": "git.clone",
"when": "config.git.enabled"
},
{
"command": "git.init",
"when": "config.git.enabled"
},
{
"command": "git.refresh",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.openFile",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.openHEADFile",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.openChange",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.stage",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.stageAll",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.stageSelectedRanges",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.revertSelectedRanges",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.unstage",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.unstageAll",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.unstageSelectedRanges",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.clean",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.cleanAll",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.commit",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.commitStaged",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.commitStagedSigned",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.commitStagedAmend",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.commitAll",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.commitAllSigned",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.commitAllAmend",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.undoCommit",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.checkout",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.branch",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.deleteBranch",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.pull",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.pullFrom",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.pullRebase",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.pullFrom",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.merge",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.createTag",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.push",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.pushTo",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.pushWithTags",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.sync",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.publish",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.showOutput",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.ignore",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.stash",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.stashPop",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.stashPopLatest",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
}
],
"scm/title": [
{
"command": "git.init",
"group": "navigation",
"when": "config.git.enabled && !scmProvider && gitOpenRepositoryCount == 0"
},
{
"command": "git.commit",
"group": "navigation",
"when": "config.git.enabled && scmProvider == git"
},
{
"command": "git.refresh",
"group": "navigation",
"when": "config.git.enabled && scmProvider == git"
},
{
"command": "git.sync",
"group": "1_sync",
"when": "config.git.enabled && scmProvider == git"
},
{
"command": "git.pull",
"group": "1_sync",
"when": "config.git.enabled && scmProvider == git"
},
{
"command": "git.pullRebase",
"group": "1_sync",
"when": "config.git.enabled && scmProvider == git"
},
{
"command": "git.pullFrom",
"group": "1_sync",
"when": "config.git.enabled && scmProvider == git"
},
{
"command": "git.push",
"group": "1_sync",
"when": "config.git.enabled && scmProvider == git"
},
{
"command": "git.pushTo",
"group": "1_sync",
"when": "config.git.enabled && scmProvider == git"
},
{
"command": "git.publish",
"group": "2_publish",
"when": "config.git.enabled && scmProvider == git"
},
{
"command": "git.commitStaged",
"group": "3_commit",
"when": "config.git.enabled && scmProvider == git"
},
{
"command": "git.commitStagedSigned",
"group": "3_commit",
"when": "config.git.enabled && scmProvider == git"
},
{
"command": "git.commitStagedAmend",
"group": "3_commit",
"when": "config.git.enabled && scmProvider == git"
},
{
"command": "git.commitAll",
"group": "3_commit",
"when": "config.git.enabled && scmProvider == git"
},
{
"command": "git.commitAllSigned",
"group": "3_commit",
"when": "config.git.enabled && scmProvider == git"
},
{
"command": "git.commitAllAmend",
"group": "3_commit",
"when": "config.git.enabled && scmProvider == git"
},
{
"command": "git.undoCommit",
"group": "3_commit",
"when": "config.git.enabled && scmProvider == git"
},
{
"command": "git.unstageAll",
"group": "4_stage",
"when": "config.git.enabled && scmProvider == git"
},
{
"command": "git.cleanAll",
"group": "4_stage",
"when": "config.git.enabled && scmProvider == git"
},
{
"command": "git.stash",
"group": "5_stash",
"when": "config.git.enabled && scmProvider == git"
},
{
"command": "git.stashPop",
"group": "5_stash",
"when": "config.git.enabled && scmProvider == git"
},
{
"command": "git.stashPopLatest",
"group": "5_stash",
"when": "config.git.enabled && scmProvider == git"
},
{
"command": "git.showOutput",
"group": "7_repository",
"when": "config.git.enabled && scmProvider == git"
}
],
"scm/resourceGroup/context": [
{
"command": "git.stageAll",
"when": "config.git.enabled && scmProvider == git && scmResourceGroup == merge",
"group": "1_modification"
},
{
"command": "git.stageAll",
"when": "config.git.enabled && scmProvider == git && scmResourceGroup == merge",
"group": "inline"
},
{
"command": "git.unstageAll",
"when": "config.git.enabled && scmProvider == git && scmResourceGroup == index",
"group": "1_modification"
},
{
"command": "git.unstageAll",
"when": "config.git.enabled && scmProvider == git && scmResourceGroup == index",
"group": "inline"
},
{
"command": "git.cleanAll",
"when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree",
"group": "1_modification"
},
{
"command": "git.stageAll",
"when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree",
"group": "1_modification"
},
{
"command": "git.cleanAll",
"when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree",
"group": "inline"
},
{
"command": "git.stageAll",
"when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree",
"group": "inline"
}
],
"scm/resourceState/context": [
{
"command": "git.stage",
"when": "config.git.enabled && scmProvider == git && scmResourceGroup == merge",
"group": "1_modification"
},
{
"command": "git.stage",
"when": "config.git.enabled && scmProvider == git && scmResourceGroup == merge",
"group": "inline"
},
{
"command": "git.openChange",
"when": "config.git.enabled && scmProvider == git && scmResourceGroup == index",
"group": "navigation"
},
{
"command": "git.openFile",
"when": "config.git.enabled && scmProvider == git && scmResourceGroup == index",
"group": "navigation"
},
{
"command": "git.openHEADFile",
"when": "config.git.enabled && scmProvider == git && scmResourceGroup == index",
"group": "navigation"
},
{
"command": "git.unstage",
"when": "config.git.enabled && scmProvider == git && scmResourceGroup == index",
"group": "1_modification"
},
{
"command": "git.unstage",
"when": "config.git.enabled && scmProvider == git && scmResourceGroup == index",
"group": "inline"
},
{
"command": "git.openChange",
"when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree",
"group": "navigation"
},
{
"command": "git.openHEADFile",
"when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree",
"group": "navigation"
},
{
"command": "git.openFile",
"when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree",
"group": "navigation"
},
{
"command": "git.stage",
"when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree",
"group": "1_modification"
},
{
"command": "git.clean",
"when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree",
"group": "1_modification"
},
{
"command": "git.clean",
"when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree",
"group": "inline"
},
{
"command": "git.stage",
"when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree",
"group": "inline"
},
{
"command": "git.ignore",
"when": "config.git.enabled && scmProvider == git && scmResourceGroup == workingTree",
"group": "1_modification@3"
}
],
"editor/title": [
{
"command": "git.openFile",
"group": "navigation",
"when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme != extension && resourceScheme != merge-conflict.conflict-diff"
},
{
"command": "git.openChange",
"group": "navigation",
"when": "config.git.enabled && gitOpenRepositoryCount != 0 && !isInDiffEditor && resourceScheme != extension"
},
{
"command": "git.stageSelectedRanges",
"group": "2_git@1",
"when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme != merge-conflict.conflict-diff"
},
{
"command": "git.unstageSelectedRanges",
"group": "2_git@2",
"when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme != merge-conflict.conflict-diff"
},
{
"command": "git.revertSelectedRanges",
"group": "2_git@3",
"when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme != merge-conflict.conflict-diff"
}
]
},
"configuration": {
"title": "Git",
"properties": {
"git.enabled": {
"type": "boolean",
"description": "%config.enabled%",
"default": true
},
"git.path": {
"type": [
"string",
"null"
],
"description": "%config.path%",
"default": null,
"isExecutable": true
},
"git.autorefresh": {
"type": "boolean",
"description": "%config.autorefresh%",
"default": true
},
"git.autofetch": {
"type": "boolean",
"description": "%config.autofetch%",
"default": true
},
"git.confirmSync": {
"type": "boolean",
"description": "%config.confirmSync%",
"default": true
},
"git.countBadge": {
"type": "string",
"enum": [
"all",
"tracked",
"off"
],
"description": "%config.countBadge%",
"default": "all"
},
"git.checkoutType": {
"type": "string",
"enum": [
"all",
"local",
"tags",
"remote"
],
"description": "%config.checkoutType%",
"default": "all"
},
"git.ignoreLegacyWarning": {
"type": "boolean",
"description": "%config.ignoreLegacyWarning%",
"default": false
},
"git.ignoreLimitWarning": {
"type": "boolean",
"description": "%config.ignoreLimitWarning%",
"default": false
},
"git.defaultCloneDirectory": {
"type": "string",
"default": null,
"description": "%config.defaultCloneDirectory%"
},
"git.enableSmartCommit": {
"type": "boolean",
"description": "%config.enableSmartCommit%",
"default": false
},
"git.enableCommitSigning": {
"type": "boolean",
"description": "%config.enableCommitSigning%",
"default": false
}
}
}
},
"dependencies": {
"iconv-lite": "0.4.15",
"vscode-extension-telemetry": "0.0.8",
"vscode-nls": "2.0.2"
},
"devDependencies": {
"@types/mocha": "^2.2.41",
"@types/node": "^7.0.4",
"mocha": "^3.2.0"
}
}

View File

@@ -0,0 +1,58 @@
{
"command.clone": "Clone",
"command.init": "Initialize Repository",
"command.close": "Close Repository",
"command.refresh": "Refresh",
"command.openChange": "Open Changes",
"command.openFile": "Open File",
"command.openHEADFile": "Open File (HEAD)",
"command.stage": "Stage Changes",
"command.stageAll": "Stage All Changes",
"command.stageSelectedRanges": "Stage Selected Ranges",
"command.revertSelectedRanges": "Revert Selected Ranges",
"command.unstage": "Unstage Changes",
"command.unstageAll": "Unstage All Changes",
"command.unstageSelectedRanges": "Unstage Selected Ranges",
"command.clean": "Discard Changes",
"command.cleanAll": "Discard All Changes",
"command.commit": "Commit",
"command.commitStaged": "Commit Staged",
"command.commitStagedSigned": "Commit Staged (Signed Off)",
"command.commitStagedAmend": "Commit Staged (Amend)",
"command.commitAll": "Commit All",
"command.commitAllSigned": "Commit All (Signed Off)",
"command.commitAllAmend": "Commit All (Amend)",
"command.undoCommit": "Undo Last Commit",
"command.checkout": "Checkout to...",
"command.branch": "Create Branch...",
"command.deleteBranch": "Delete Branch...",
"command.merge": "Merge Branch...",
"command.createTag": "Create Tag",
"command.pull": "Pull",
"command.pullRebase": "Pull (Rebase)",
"command.pullFrom": "Pull from...",
"command.push": "Push",
"command.pushTo": "Push to...",
"command.pushWithTags": "Push With Tags",
"command.sync": "Sync",
"command.publish": "Publish Branch",
"command.showOutput": "Show Git Output",
"command.ignore": "Add File to .gitignore",
"command.stash": "Stash",
"command.stashPop": "Pop Stash...",
"command.stashPopLatest": "Pop Latest Stash",
"config.enabled": "Whether git is enabled",
"config.path": "Path to the git executable",
"config.autorefresh": "Whether auto refreshing is enabled",
"config.autofetch": "Whether auto fetching is enabled",
"config.enableLongCommitWarning": "Whether long commit messages should be warned about",
"config.confirmSync": "Confirm before synchronizing git repositories",
"config.countBadge": "Controls the git badge counter. `all` counts all changes. `tracked` counts only the tracked changes. `off` turns it off.",
"config.checkoutType": "Controls what type of branches are listed when running `Checkout to...`. `all` shows all refs, `local` shows only the local branchs, `tags` shows only tags and `remote` shows only remote branches.",
"config.ignoreLegacyWarning": "Ignores the legacy Git warning",
"config.ignoreLimitWarning": "Ignores the warning when there are too many changes in a repository",
"config.defaultCloneDirectory": "The default location where to clone a git repository",
"config.enableSmartCommit": "Commit all changes when there are no staged changes.",
"config.enableCommitSigning": "Enables commit signing with GPG.",
"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."
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="-2 -2 16 16" enable-background="new -2 -2 16 16"><polygon fill="#C5C5C5" points="9,0 4.5,9 3,6 0,6 3,12 6,12 12,0"/></svg>

After

Width:  |  Height:  |  Size: 194 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="-0.994 0 16 16" enable-background="new -0.994 0 16 16"><path fill="#C5C5C5" d="M13 6c0 1.461-.636 2.846-1.746 3.797l-5.584 4.951-1.324-1.496 5.595-4.962c.678-.582 1.061-1.413 1.061-2.29 0-1.654-1.345-3-2.997-3-.71 0-1.399.253-1.938.713l-1.521 1.287h2.448l-1.998 2h-3.996v-4l1.998-2v2.692l1.775-1.504c.899-.766 2.047-1.188 3.232-1.188 2.754 0 4.995 2.243 4.995 5z"/></svg>

After

Width:  |  Height:  |  Size: 443 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><path d="M27.459 14.902l-10.439-10.439c-.296-.294-.672-.452-1.089-.452-.417 0-.793.157-1.089.452l-2.248 2.247 2.549 2.549c.249-.112.522-.177.813-.177 1.106 0 2.002.896 2.002 2.002 0 .291-.064.565-.176.814l2.311 2.336c.25-.111.633-.234.923-.234 1.106 0 2 .911 2 2.016s-.894 1.969-2 1.969c-1.105-.001-2.016-.751-2.016-1.985 0-.28.016-.462.119-.704l-2.373-2.374-.023.007v6.274c.747.295 1.277 1.026 1.277 1.875 0 1.105-.878 2.016-1.984 2.016-1.104 0-2.031-.926-2.031-2.031 0-.846.535-1.564 1.28-1.857l.001-6.25c-.762-.282-1.309-1.009-1.309-1.871 0-.28.059-.546.162-.788l-2.555-2.557-7.115 7.114c-.599.601-.601 1.576.001 2.178l10.44 10.518c.296.295.671.45 1.089.45.415 0 .796-.159 1.089-.45l10.391-10.471c.601-.599.599-1.576 0-2.177z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 812 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#C5C5C5" d="M15 4v6h-2.276c.113-.318.187-.653.226-1h1.05v-5h-2v-2h-4v2.051c-.347.038-.681.112-1 .225v-3.276h5l3 3zm-7 8.949v1.051h-6v-7h2.276c.126-.354.28-.693.485-1h-3.761v9h8v-2.051c-.166.02-.329.051-.5.051l-.5-.051z"/><path fill="#75BEFF" d="M12 8.5c0-1.933-1.567-3.5-3.5-3.5s-3.5 1.567-3.5 3.5 1.567 3.5 3.5 3.5c.711 0 1.369-.215 1.922-.578l3.578 3.578 1-1-3.578-3.578c.363-.553.578-1.211.578-1.922zm-3.5 2.5c-1.381 0-2.5-1.119-2.5-2.5s1.119-2.5 2.5-2.5 2.5 1.119 2.5 2.5-1.119 2.5-2.5 2.5z"/></svg>

After

Width:  |  Height:  |  Size: 578 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><polygon fill="#C5C5C5" points="10,2 7.414,2 8.414,3 9,3 9,3.586 9,4 9,4.414 9,6 12,6 12,13 4,13 4,8 3,8 3,14 13,14 13,5"/><polygon fill="#75BEFF" points="5,1 3,1 5,3 1,3 1,5 5,5 3,7 5,7 8,4"/></svg>

After

Width:  |  Height:  |  Size: 262 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M13.451 5.609l-.579-.939-1.068.812-.076.094c-.335.415-.927 1.341-1.124 2.876l-.021.165.033.163.071.345c0 1.654-1.346 3-3 3-.795 0-1.545-.311-2.107-.868-.563-.567-.873-1.317-.873-2.111 0-1.431 1.007-2.632 2.351-2.929v2.926s2.528-2.087 2.984-2.461h.012l3.061-2.582-4.919-4.1h-1.137v2.404c-3.429.318-6.121 3.211-6.121 6.721 0 1.809.707 3.508 1.986 4.782 1.277 1.282 2.976 1.988 4.784 1.988 3.722 0 6.75-3.028 6.75-6.75 0-1.245-.349-2.468-1.007-3.536z" fill="#2D2D30"/><path d="M12.6 6.134l-.094.071c-.269.333-.746 1.096-.91 2.375.057.277.092.495.092.545 0 2.206-1.794 4-4 4-1.098 0-2.093-.445-2.817-1.164-.718-.724-1.163-1.718-1.163-2.815 0-2.206 1.794-4 4-4l.351.025v1.85s1.626-1.342 1.631-1.339l1.869-1.577-3.5-2.917v2.218l-.371-.03c-3.176 0-5.75 2.574-5.75 5.75 0 1.593.648 3.034 1.695 4.076 1.042 1.046 2.482 1.694 4.076 1.694 3.176 0 5.75-2.574 5.75-5.75-.001-1.106-.318-2.135-.859-3.012z" fill="#C5C5C5"/></svg>

After

Width:  |  Height:  |  Size: 986 B

View File

@@ -0,0 +1 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><title>Layer 1</title><rect height="11" width="3" y="3" x="7" fill="#C5C5C5"/><rect height="3" width="11" y="7" x="3" fill="#C5C5C5"/></svg>

After

Width:  |  Height:  |  Size: 203 B

View File

@@ -0,0 +1,6 @@
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#3c8746" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
A
</text>
</svg>

After

Width:  |  Height:  |  Size: 431 B

View File

@@ -0,0 +1,6 @@
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#7F4E7E" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
C
</text>
</svg>

After

Width:  |  Height:  |  Size: 431 B

View File

@@ -0,0 +1,6 @@
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#692C77" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
C
</text>
</svg>

After

Width:  |  Height:  |  Size: 431 B

View File

@@ -0,0 +1,6 @@
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#9E121D" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
D
</text>
</svg>

After

Width:  |  Height:  |  Size: 431 B

View File

@@ -0,0 +1,6 @@
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#969696" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
I
</text>
</svg>

After

Width:  |  Height:  |  Size: 431 B

View File

@@ -0,0 +1,6 @@
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#1B80B2" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
M
</text>
</svg>

After

Width:  |  Height:  |  Size: 431 B

View File

@@ -0,0 +1,6 @@
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#CC6633" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
R
</text>
</svg>

After

Width:  |  Height:  |  Size: 431 B

View File

@@ -0,0 +1,6 @@
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#6C6C6C" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
U
</text>
</svg>

After

Width:  |  Height:  |  Size: 431 B

View File

@@ -0,0 +1 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><title>Layer 1</title><rect height="3" width="11" y="7" x="3" fill="#C5C5C5"/></svg>

After

Width:  |  Height:  |  Size: 147 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><polygon points="5.382,13 2.382,7 6.618,7 7,7.764 9.382,3 13.618,3 8.618,13" fill="#F6F6F6"/><path d="M12 4l-4 8h-2l-2-4h2l1 2 3-6h2z" fill="#424242"/></svg>

After

Width:  |  Height:  |  Size: 220 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="-0.994 0 16 16" enable-background="new -0.994 0 16 16"><path fill="#424242" d="M13 6c0 1.461-.636 2.846-1.746 3.797l-5.584 4.951-1.324-1.496 5.595-4.962c.678-.582 1.061-1.413 1.061-2.29 0-1.654-1.345-3-2.997-3-.71 0-1.399.253-1.938.713l-1.521 1.287h2.448l-1.998 2h-3.996v-4l1.998-2v2.692l1.775-1.504c.899-.766 2.047-1.188 3.232-1.188 2.754 0 4.995 2.243 4.995 5z"/></svg>

After

Width:  |  Height:  |  Size: 443 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><defs><clipPath><path d="M.06 91.886h91.828v-91.886h-91.828v91.886z"/></clipPath><clipPath id="a"><path d="M0 0h92v92h-92v-92z"/></clipPath></defs><path d="M28.497 14.84l-11.354-11.353c-.653-.654-1.714-.654-2.368 0l-2.357 2.358 2.99 2.991c.695-.235 1.492-.077 2.046.477.557.558.713 1.361.473 2.059l2.882 2.882c.698-.241 1.502-.085 2.059.473.778.778.778 2.039 0 2.818-.779.779-2.04.779-2.819 0-.586-.586-.73-1.446-.434-2.167l-2.688-2.688v7.074c.19.094.369.219.527.377.778.778.778 2.039 0 2.819-.778.778-2.04.778-2.818 0-.778-.779-.778-2.04 0-2.819.192-.192.415-.338.653-.435v-7.14c-.237-.097-.46-.241-.653-.435-.589-.589-.731-1.455-.429-2.179l-2.948-2.949-7.785 7.785c-.654.655-.654 1.715 0 2.369l11.354 11.353c.654.654 1.714.654 2.369 0l11.3-11.301c.654-.654.654-1.715 0-2.369" fill="#424242" clip-path="url(#a)"/></svg>

After

Width:  |  Height:  |  Size: 883 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#656565" d="M15 4v6h-2.276c.113-.318.187-.653.226-1h1.05v-5h-2v-2h-4v2.051c-.347.038-.681.112-1 .225v-3.276h5l3 3zm-7 8.949v1.051h-6v-7h2.276c.126-.354.28-.693.485-1h-3.761v9h8v-2.051c-.166.02-.329.051-.5.051l-.5-.051z"/><path fill="#00539C" d="M12 8.5c0-1.933-1.567-3.5-3.5-3.5s-3.5 1.567-3.5 3.5 1.567 3.5 3.5 3.5c.711 0 1.369-.215 1.922-.578l3.578 3.578 1-1-3.578-3.578c.363-.553.578-1.211.578-1.922zm-3.5 2.5c-1.381 0-2.5-1.119-2.5-2.5s1.119-2.5 2.5-2.5 2.5 1.119 2.5 2.5-1.119 2.5-2.5 2.5z"/></svg>

After

Width:  |  Height:  |  Size: 578 B

View File

@@ -0,0 +1,3 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><polygon fill="#656565" points="10,2 7.414,2 8.414,3 9,3 9,3.586 9,4 9,4.414 9,6 12,6 12,13 4,13 4,8 3,8 3,14 13,14 13,5"/><polygon fill="#00539C" points="5,1 3,1 5,3 1,3 1,5 5,5 3,7 5,7 8,4"/></svg>

After

Width:  |  Height:  |  Size: 417 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M13.451 5.609l-.579-.939-1.068.812-.076.094c-.335.415-.927 1.341-1.124 2.876l-.021.165.033.163.071.345c0 1.654-1.346 3-3 3-.795 0-1.545-.311-2.107-.868-.563-.567-.873-1.317-.873-2.111 0-1.431 1.007-2.632 2.351-2.929v2.926s2.528-2.087 2.984-2.461h.012l3.061-2.582-4.919-4.1h-1.137v2.404c-3.429.318-6.121 3.211-6.121 6.721 0 1.809.707 3.508 1.986 4.782 1.277 1.282 2.976 1.988 4.784 1.988 3.722 0 6.75-3.028 6.75-6.75 0-1.245-.349-2.468-1.007-3.536z" fill="#F6F6F6"/><path d="M12.6 6.134l-.094.071c-.269.333-.746 1.096-.91 2.375.057.277.092.495.092.545 0 2.206-1.794 4-4 4-1.098 0-2.093-.445-2.817-1.164-.718-.724-1.163-1.718-1.163-2.815 0-2.206 1.794-4 4-4l.351.025v1.85s1.626-1.342 1.631-1.339l1.869-1.577-3.5-2.917v2.218l-.371-.03c-3.176 0-5.75 2.574-5.75 5.75 0 1.593.648 3.034 1.695 4.076 1.042 1.046 2.482 1.694 4.076 1.694 3.176 0 5.75-2.574 5.75-5.75-.001-1.106-.318-2.135-.859-3.012z" fill="#424242"/></svg>

After

Width:  |  Height:  |  Size: 986 B

View File

@@ -0,0 +1 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><title>Layer 1</title><rect height="11" width="3" y="3" x="7" fill="#424242"/><rect height="3" width="11" y="7" x="3" fill="#424242"/></svg>

After

Width:  |  Height:  |  Size: 203 B

View File

@@ -0,0 +1,6 @@
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#2d883e" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
A
</text>
</svg>

After

Width:  |  Height:  |  Size: 431 B

View File

@@ -0,0 +1,6 @@
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#9B4F96" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
C
</text>
</svg>

After

Width:  |  Height:  |  Size: 431 B

View File

@@ -0,0 +1,6 @@
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#682079" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
C
</text>
</svg>

After

Width:  |  Height:  |  Size: 431 B

View File

@@ -0,0 +1,6 @@
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#B9131A" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
D
</text>
</svg>

After

Width:  |  Height:  |  Size: 431 B

View File

@@ -0,0 +1,6 @@
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#969696" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
I
</text>
</svg>

After

Width:  |  Height:  |  Size: 431 B

View File

@@ -0,0 +1,6 @@
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#007ACC" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
M
</text>
</svg>

After

Width:  |  Height:  |  Size: 431 B

View File

@@ -0,0 +1,6 @@
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#CC6633" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
R
</text>
</svg>

After

Width:  |  Height:  |  Size: 431 B

View File

@@ -0,0 +1,6 @@
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#6C6C6C" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
U
</text>
</svg>

After

Width:  |  Height:  |  Size: 431 B

View File

@@ -0,0 +1 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><title>Layer 1</title><rect height="3" width="11" y="7" x="3" fill="#424242"/></svg>

After

Width:  |  Height:  |  Size: 147 B

View File

@@ -0,0 +1,2 @@
#!/bin/sh
echo ''

View File

@@ -0,0 +1,74 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as http from 'http';
import * as fs from 'fs';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
function fatal(err: any): void {
console.error(localize('missOrInvalid', "Missing or invalid credentials."));
console.error(err);
process.exit(1);
}
function main(argv: string[]): void {
if (argv.length !== 5) {
return fatal('Wrong number of arguments');
}
if (!process.env['VSCODE_GIT_ASKPASS_HANDLE']) {
return fatal('Missing handle');
}
if (!process.env['VSCODE_GIT_ASKPASS_PIPE']) {
return fatal('Missing pipe');
}
if (process.env['VSCODE_GIT_COMMAND'] === 'fetch') {
return fatal('Skip fetch commands');
}
const output = process.env['VSCODE_GIT_ASKPASS_PIPE'];
const socketPath = process.env['VSCODE_GIT_ASKPASS_HANDLE'];
const request = argv[2];
const host = argv[4].substring(1, argv[4].length - 2);
const opts: http.RequestOptions = {
socketPath,
path: '/',
method: 'POST'
};
const req = http.request(opts, res => {
if (res.statusCode !== 200) {
return fatal(`Bad status code: ${res.statusCode}`);
}
const chunks: string[] = [];
res.setEncoding('utf8');
res.on('data', (d: string) => chunks.push(d));
res.on('end', () => {
const raw = chunks.join('');
try {
const result = JSON.parse(raw);
fs.writeFileSync(output, result + '\n');
} catch (err) {
return fatal(`Error parsing response`);
}
setTimeout(() => process.exit(0), 0);
});
});
req.on('error', () => fatal('Error in request'));
req.write(JSON.stringify({ request, host }));
req.end();
}
main(process.argv);

View File

@@ -0,0 +1,5 @@
#!/bin/sh
VSCODE_GIT_ASKPASS_PIPE=`mktemp`
VSCODE_GIT_ASKPASS_PIPE="$VSCODE_GIT_ASKPASS_PIPE" "$VSCODE_GIT_ASKPASS_NODE" "$VSCODE_GIT_ASKPASS_MAIN" $*
cat $VSCODE_GIT_ASKPASS_PIPE
rm $VSCODE_GIT_ASKPASS_PIPE

View File

@@ -0,0 +1,114 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Disposable, window, InputBoxOptions } from 'vscode';
import { denodeify } from './util';
import * as path from 'path';
import * as http from 'http';
import * as os from 'os';
import * as crypto from 'crypto';
const randomBytes = denodeify<Buffer>(crypto.randomBytes);
export interface AskpassEnvironment {
GIT_ASKPASS: string;
ELECTRON_RUN_AS_NODE?: string;
VSCODE_GIT_ASKPASS_NODE?: string;
VSCODE_GIT_ASKPASS_MAIN?: string;
VSCODE_GIT_ASKPASS_HANDLE?: string;
}
function getIPCHandlePath(nonce: string): string {
if (process.platform === 'win32') {
return `\\\\.\\pipe\\vscode-git-askpass-${nonce}-sock`;
}
if (process.env['XDG_RUNTIME_DIR']) {
return path.join(process.env['XDG_RUNTIME_DIR'], `vscode-git-askpass-${nonce}.sock`);
}
return path.join(os.tmpdir(), `vscode-git-askpass-${nonce}.sock`);
}
export class Askpass implements Disposable {
private server: http.Server;
private ipcHandlePathPromise: Promise<string>;
private enabled = true;
constructor() {
this.server = http.createServer((req, res) => this.onRequest(req, res));
this.ipcHandlePathPromise = this.setup().catch(err => {
console.error(err);
return '';
});
}
private async setup(): Promise<string> {
const buffer = await randomBytes(20);
const nonce = buffer.toString('hex');
const ipcHandlePath = getIPCHandlePath(nonce);
try {
this.server.listen(ipcHandlePath);
this.server.on('error', err => console.error(err));
} catch (err) {
console.error('Could not launch git askpass helper.');
this.enabled = false;
}
return ipcHandlePath;
}
private onRequest(req: http.ServerRequest, res: http.ServerResponse): void {
const chunks: string[] = [];
req.setEncoding('utf8');
req.on('data', (d: string) => chunks.push(d));
req.on('end', () => {
const { request, host } = JSON.parse(chunks.join(''));
this.prompt(host, request).then(result => {
res.writeHead(200);
res.end(JSON.stringify(result));
}, () => {
res.writeHead(500);
res.end();
});
});
}
private async prompt(host: string, request: string): Promise<string> {
const options: InputBoxOptions = {
password: /password/i.test(request),
placeHolder: request,
prompt: `Git: ${host}`,
ignoreFocusOut: true
};
return await window.showInputBox(options) || '';
}
async getEnv(): Promise<AskpassEnvironment> {
if (!this.enabled) {
return {
GIT_ASKPASS: path.join(__dirname, 'askpass-empty.sh')
};
}
return {
ELECTRON_RUN_AS_NODE: '1',
GIT_ASKPASS: path.join(__dirname, 'askpass.sh'),
VSCODE_GIT_ASKPASS_NODE: process.execPath,
VSCODE_GIT_ASKPASS_MAIN: path.join(__dirname, 'askpass-main.js'),
VSCODE_GIT_ASKPASS_HANDLE: await this.ipcHandlePathPromise
};
}
dispose(): void {
this.server.close();
}
}

View File

@@ -0,0 +1,62 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { workspace, Disposable } from 'vscode';
import { GitErrorCodes } from './git';
import { Repository } from './repository';
import { throttle } from './decorators';
export class AutoFetcher {
private static Period = 3 * 60 * 1000 /* three minutes */;
private disposables: Disposable[] = [];
private timer: NodeJS.Timer;
constructor(private repository: Repository) {
workspace.onDidChangeConfiguration(this.onConfiguration, this, this.disposables);
this.onConfiguration();
}
private onConfiguration(): void {
const gitConfig = workspace.getConfiguration('git');
if (gitConfig.get<boolean>('autofetch') === false) {
this.disable();
} else {
this.enable();
}
}
enable(): void {
if (this.timer) {
return;
}
this.fetch();
this.timer = setInterval(() => this.fetch(), AutoFetcher.Period);
}
disable(): void {
clearInterval(this.timer);
}
@throttle
private async fetch(): Promise<void> {
try {
await this.repository.fetch();
} catch (err) {
if (err.gitErrorCode === GitErrorCodes.AuthenticationFailed) {
this.disable();
}
}
}
dispose(): void {
this.disable();
this.disposables.forEach(d => d.dispose());
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,117 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { workspace, Uri, Disposable, Event, EventEmitter, window } from 'vscode';
import { debounce } from './decorators';
import { fromGitUri } from './uri';
import { Model, ModelChangeEvent } from './model';
interface CacheRow {
uri: Uri;
timestamp: number;
}
interface Cache {
[uri: string]: CacheRow;
}
const THREE_MINUTES = 1000 * 60 * 3;
const FIVE_MINUTES = 1000 * 60 * 5;
export class GitContentProvider {
private onDidChangeEmitter = new EventEmitter<Uri>();
get onDidChange(): Event<Uri> { return this.onDidChangeEmitter.event; }
private changedRepositoryRoots = new Set<string>();
private cache: Cache = Object.create(null);
private disposables: Disposable[] = [];
constructor(private model: Model) {
this.disposables.push(
model.onDidChangeRepository(this.onDidChangeRepository, this),
workspace.registerTextDocumentContentProvider('git', this)
);
setInterval(() => this.cleanup(), FIVE_MINUTES);
}
private onDidChangeRepository({ repository }: ModelChangeEvent): void {
this.changedRepositoryRoots.add(repository.root);
this.eventuallyFireChangeEvents();
}
@debounce(1100)
private eventuallyFireChangeEvents(): void {
this.fireChangeEvents();
}
private fireChangeEvents(): void {
Object.keys(this.cache).forEach(key => {
const uri = this.cache[key].uri;
const fsPath = uri.fsPath;
for (const root of this.changedRepositoryRoots) {
if (fsPath.startsWith(root)) {
this.onDidChangeEmitter.fire(uri);
return;
}
}
});
this.changedRepositoryRoots.clear();
}
async provideTextDocumentContent(uri: Uri): Promise<string> {
const repository = this.model.getRepository(uri);
if (!repository) {
return '';
}
const cacheKey = uri.toString();
const timestamp = new Date().getTime();
const cacheValue = { uri, timestamp };
this.cache[cacheKey] = cacheValue;
let { path, ref } = fromGitUri(uri);
if (ref === '~') {
const fileUri = Uri.file(path);
const uriString = fileUri.toString();
const [indexStatus] = repository.indexGroup.resourceStates.filter(r => r.original.toString() === uriString);
ref = indexStatus ? '' : 'HEAD';
}
try {
return await repository.show(ref, path);
} catch (err) {
return '';
}
}
private cleanup(): void {
const now = new Date().getTime();
const cache = Object.create(null);
Object.keys(this.cache).forEach(key => {
const row = this.cache[key];
const isOpen = window.visibleTextEditors.some(e => e.document.uri.fsPath === row.uri.fsPath);
if (isOpen || now - row.timestamp < THREE_MINUTES) {
cache[row.uri.toString()] = row;
}
});
this.cache = cache;
}
dispose(): void {
this.disposables.forEach(d => d.dispose());
}
}

View File

@@ -0,0 +1,103 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { done } from './util';
function decorate(decorator: (fn: Function, key: string) => Function): Function {
return (target: any, key: string, descriptor: any) => {
let fnKey: string | null = null;
let fn: Function | null = null;
if (typeof descriptor.value === 'function') {
fnKey = 'value';
fn = descriptor.value;
} else if (typeof descriptor.get === 'function') {
fnKey = 'get';
fn = descriptor.get;
}
if (!fn || !fnKey) {
throw new Error('not supported');
}
descriptor[fnKey] = decorator(fn, key);
};
}
function _memoize(fn: Function, key: string): Function {
const memoizeKey = `$memoize$${key}`;
return function (...args: any[]) {
if (!this.hasOwnProperty(memoizeKey)) {
Object.defineProperty(this, memoizeKey, {
configurable: false,
enumerable: false,
writable: false,
value: fn.apply(this, args)
});
}
return this[memoizeKey];
};
}
export const memoize = decorate(_memoize);
function _throttle<T>(fn: Function, key: string): Function {
const currentKey = `$throttle$current$${key}`;
const nextKey = `$throttle$next$${key}`;
const trigger = function (...args: any[]) {
if (this[nextKey]) {
return this[nextKey];
}
if (this[currentKey]) {
this[nextKey] = done(this[currentKey]).then(() => {
this[nextKey] = undefined;
return trigger.apply(this, args);
});
return this[nextKey];
}
this[currentKey] = fn.apply(this, args) as Promise<T>;
const clear = () => this[currentKey] = undefined;
done(this[currentKey]).then(clear, clear);
return this[currentKey];
};
return trigger;
}
export const throttle = decorate(_throttle);
function _sequentialize<T>(fn: Function, key: string): Function {
const currentKey = `__$sequence$${key}`;
return function (...args: any[]) {
const currentPromise = this[currentKey] as Promise<any> || Promise.resolve(null);
const run = async () => await fn.apply(this, args);
this[currentKey] = currentPromise.then(run, run);
return this[currentKey];
};
}
export const sequentialize = decorate(_sequentialize);
export function debounce(delay: number): Function {
return decorate((fn, key) => {
const timerKey = `$debounce$${key}`;
return function (...args: any[]) {
clearTimeout(this[timerKey]);
this[timerKey] = setTimeout(() => fn.apply(this, args), delay);
};
});
}

1067
extensions/git/src/git.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,57 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
function* filter<T>(it: IterableIterator<T>, condition: (t: T, i: number) => boolean): IterableIterator<T> {
let i = 0;
for (let t of it) {
if (condition(t, i++)) {
yield t;
}
}
}
function* map<T, R>(it: IterableIterator<T>, fn: (t: T, i: number) => R): IterableIterator<R> {
let i = 0;
for (let t of it) {
yield fn(t, i++);
}
}
export interface FunctionalIterator<T> extends Iterable<T> {
filter(condition: (t: T, i: number) => boolean): FunctionalIterator<T>;
map<R>(fn: (t: T, i: number) => R): FunctionalIterator<R>;
toArray(): T[];
}
class FunctionalIteratorImpl<T> implements FunctionalIterator<T> {
constructor(private iterator: IterableIterator<T>) { }
filter(condition: (t: T, i: number) => boolean): FunctionalIterator<T> {
return new FunctionalIteratorImpl(filter(this.iterator, condition));
}
map<R>(fn: (t: T, i: number) => R): FunctionalIterator<R> {
return new FunctionalIteratorImpl(map<T, R>(this.iterator, fn));
}
toArray(): T[] {
return Array.from(this.iterator);
}
[Symbol.iterator](): IterableIterator<T> {
return this.iterator;
}
}
export function iterate<T>(obj: T[] | IterableIterator<T>): FunctionalIterator<T> {
if (Array.isArray(obj)) {
return new FunctionalIteratorImpl(obj[Symbol.iterator]());
}
return new FunctionalIteratorImpl(obj);
}

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