SQL Operations Studio Public Preview 1 (0.23) release source code
0
extensions/account-provider-azure/.gitignore
vendored
Normal file
34
extensions/account-provider-azure/package.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
3
extensions/account-provider-azure/package.nls.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extension.clearTokenCache": "Clear Azure Account Token Cache"
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -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}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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};
|
||||
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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*/];
|
||||
@@ -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;
|
||||
}
|
||||
12
extensions/account-provider-azure/src/constants.ts
Normal 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');
|
||||
35
extensions/account-provider-azure/src/main.ts
Normal 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();
|
||||
}
|
||||
7
extensions/account-provider-azure/src/typings/ref.d.ts
vendored
Normal 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'/>
|
||||
20
extensions/account-provider-azure/tsconfig.json
Normal 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"
|
||||
]
|
||||
}
|
||||
1
extensions/bat/.vscodeignore
Normal file
@@ -0,0 +1 @@
|
||||
test/**
|
||||
22
extensions/bat/OSSREADME.json
Normal 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\"."
|
||||
]
|
||||
}]
|
||||
22
extensions/bat/language-configuration.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"comments": {
|
||||
"lineComment": "REM"
|
||||
},
|
||||
"brackets": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"]
|
||||
],
|
||||
"autoClosingPairs": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""]
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""]
|
||||
]
|
||||
}
|
||||
19
extensions/bat/package.json
Normal 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"
|
||||
}]
|
||||
}
|
||||
}
|
||||
169
extensions/bat/syntaxes/Batch File.tmLanguage
Normal 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>&>|\d*>&\d*|\d*(>>|>|<)|\d*<&|\d*<></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>
|
||||
24
extensions/bat/test/colorize-fixtures/test.bat
Normal 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
|
||||
343
extensions/bat/test/colorize-results/test_bat.json
Normal 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"
|
||||
}
|
||||
}
|
||||
]
|
||||
4
extensions/configuration-editing/.vscodeignore
Normal file
@@ -0,0 +1,4 @@
|
||||
test/**
|
||||
src/**
|
||||
tsconfig.json
|
||||
npm-shrinkwrap.json
|
||||
15
extensions/configuration-editing/npm-shrinkwrap.json
generated
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
87
extensions/configuration-editing/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
183
extensions/configuration-editing/src/extension.ts
Normal 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;
|
||||
}
|
||||
});
|
||||
209
extensions/configuration-editing/src/settingsDocumentHelper.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
7
extensions/configuration-editing/src/typings/ref.d.ts
vendored
Normal 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'/>
|
||||
14
extensions/configuration-editing/tsconfig.json
Normal 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
@@ -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;
|
||||
1
extensions/diff/.vscodeignore
Normal file
@@ -0,0 +1 @@
|
||||
test/**
|
||||
22
extensions/diff/OSSREADME.json
Normal 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\"."
|
||||
]
|
||||
}]
|
||||
11
extensions/diff/language-configuration.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"comments": {
|
||||
"lineComment": "//",
|
||||
"blockComment": [ "/*", "*/" ]
|
||||
},
|
||||
"brackets": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"]
|
||||
]
|
||||
}
|
||||
26
extensions/diff/package.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
268
extensions/diff/syntaxes/diff.tmLanguage
Normal 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>^(((>)( .*)?)|((\+).*))$\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>^(((<)( .*)?)|((-).*))$\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>
|
||||
168
extensions/diff/syntaxes/diff.tmLanguage.json
Normal 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"
|
||||
}
|
||||
19
extensions/diff/test/colorize-fixtures/test.diff
Normal 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!
|
||||
398
extensions/diff/test/colorize-results/test_diff.json
Normal 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"
|
||||
}
|
||||
}
|
||||
]
|
||||
1
extensions/docker/.vscodeignore
Normal file
@@ -0,0 +1 @@
|
||||
test/**
|
||||
8
extensions/docker/OSSREADME.json
Normal 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."
|
||||
}]
|
||||
24
extensions/docker/language-configuration.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"comments": {
|
||||
"lineComment": "#"
|
||||
},
|
||||
"brackets": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"]
|
||||
],
|
||||
"autoClosingPairs": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["'", "'"]
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["'", "'"]
|
||||
]
|
||||
}
|
||||
23
extensions/docker/package.json
Normal 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"
|
||||
}]
|
||||
}
|
||||
}
|
||||
106
extensions/docker/syntaxes/docker.tmLanguage.json
Normal 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"
|
||||
}
|
||||
14
extensions/docker/test/colorize-fixtures/Dockerfile
Normal 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"]
|
||||
310
extensions/docker/test/colorize-results/Dockerfile.json
Normal 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"
|
||||
}
|
||||
}
|
||||
]
|
||||
4
extensions/extension-editing/.vscodeignore
Normal file
@@ -0,0 +1,4 @@
|
||||
test/**
|
||||
src/**
|
||||
tsconfig.json
|
||||
npm-shrinkwrap.json
|
||||
61
extensions/extension-editing/npm-shrinkwrap.json
generated
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
52
extensions/extension-editing/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
120
extensions/extension-editing/src/extension.ts
Normal 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
361
extensions/extension-editing/src/extensionLinter.ts
Normal 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('');
|
||||
}
|
||||
}
|
||||
}
|
||||
84
extensions/extension-editing/src/packageDocumentHelper.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
6
extensions/extension-editing/src/typings/ref.d.ts
vendored
Normal 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'/>
|
||||
13
extensions/extension-editing/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"es2015"
|
||||
],
|
||||
"module": "commonjs",
|
||||
"outDir": "./out"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
||||
5
extensions/git/.vscodeignore
Normal file
@@ -0,0 +1,5 @@
|
||||
src/**
|
||||
test/**
|
||||
out/test/**
|
||||
tsconfig.json
|
||||
npm-shrinkwrap.json
|
||||
29
extensions/git/OSSREADME.json
Normal 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
@@ -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
@@ -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"
|
||||
}
|
||||
}
|
||||
58
extensions/git/package.nls.json
Normal 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."
|
||||
}
|
||||
1
extensions/git/resources/icons/dark/check.svg
Normal 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 |
1
extensions/git/resources/icons/dark/clean.svg
Normal 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 |
1
extensions/git/resources/icons/dark/git.svg
Normal 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 |
1
extensions/git/resources/icons/dark/open-change.svg
Normal 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 |
1
extensions/git/resources/icons/dark/open-file.svg
Normal 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 |
1
extensions/git/resources/icons/dark/refresh.svg
Normal 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 |
1
extensions/git/resources/icons/dark/stage.svg
Normal 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 |
6
extensions/git/resources/icons/dark/status-added.svg
Normal 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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
A
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 431 B |
6
extensions/git/resources/icons/dark/status-conflict.svg
Normal 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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
C
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 431 B |
6
extensions/git/resources/icons/dark/status-copied.svg
Normal 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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
C
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 431 B |
6
extensions/git/resources/icons/dark/status-deleted.svg
Normal 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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
D
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 431 B |
6
extensions/git/resources/icons/dark/status-ignored.svg
Normal 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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
I
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 431 B |
6
extensions/git/resources/icons/dark/status-modified.svg
Normal 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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
M
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 431 B |
6
extensions/git/resources/icons/dark/status-renamed.svg
Normal 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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
R
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 431 B |
6
extensions/git/resources/icons/dark/status-untracked.svg
Normal 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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
U
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 431 B |
1
extensions/git/resources/icons/dark/unstage.svg
Normal 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 |
1
extensions/git/resources/icons/light/check.svg
Normal 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 |
1
extensions/git/resources/icons/light/clean.svg
Normal 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 |
1
extensions/git/resources/icons/light/git.svg
Normal 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 |
1
extensions/git/resources/icons/light/open-change.svg
Normal 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 |
3
extensions/git/resources/icons/light/open-file.svg
Normal 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 |
1
extensions/git/resources/icons/light/refresh.svg
Normal 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 |
1
extensions/git/resources/icons/light/stage.svg
Normal 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 |
6
extensions/git/resources/icons/light/status-added.svg
Normal 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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
A
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 431 B |
6
extensions/git/resources/icons/light/status-conflict.svg
Normal 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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
C
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 431 B |
6
extensions/git/resources/icons/light/status-copied.svg
Normal 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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
C
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 431 B |
6
extensions/git/resources/icons/light/status-deleted.svg
Normal 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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
D
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 431 B |
6
extensions/git/resources/icons/light/status-ignored.svg
Normal 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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
I
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 431 B |
6
extensions/git/resources/icons/light/status-modified.svg
Normal 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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
M
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 431 B |
6
extensions/git/resources/icons/light/status-renamed.svg
Normal 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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
R
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 431 B |
@@ -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, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
U
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 431 B |
1
extensions/git/resources/icons/light/unstage.svg
Normal 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 |
2
extensions/git/src/askpass-empty.sh
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
echo ''
|
||||
74
extensions/git/src/askpass-main.ts
Normal 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);
|
||||
5
extensions/git/src/askpass.sh
Normal 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
|
||||
114
extensions/git/src/askpass.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
62
extensions/git/src/autofetch.ts
Normal 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());
|
||||
}
|
||||
}
|
||||
1412
extensions/git/src/commands.ts
Normal file
117
extensions/git/src/contentProvider.ts
Normal 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());
|
||||
}
|
||||
}
|
||||
103
extensions/git/src/decorators.ts
Normal 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
57
extensions/git/src/iterators.ts
Normal 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);
|
||||
}
|
||||