Refresh master with initial release/0.24 snapshot (#332)

* Initial port of release/0.24 source code

* Fix additional headers

* Fix a typo in launch.json
This commit is contained in:
Karl Burtram
2017-12-15 15:38:57 -08:00
committed by GitHub
parent 271b3a0b82
commit 6ad0df0e3e
7118 changed files with 107999 additions and 56466 deletions
+8 -2
View File
@@ -18,5 +18,11 @@
["[", "]"],
["(", ")"],
["\"", "\""]
]
}
],
"folding": {
"markers": {
"start": "^\\s*(::\\s*|REM\\s+)#region",
"end": "^\\s*(::\\s*|REM\\s+)#endregion"
}
}
}
+8 -16
View File
@@ -33,19 +33,11 @@
},
{
"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"
"url": "vscode://schemas/settings/default"
},
{
"fileMatch": "%APP_SETTINGS_HOME%/settings.json",
"url": "vscode://schemas/settings"
"url": "vscode://schemas/settings/user"
},
{
"fileMatch": "%APP_WORKSPACES_HOME%/*/workspace.json",
@@ -60,15 +52,15 @@
"url": "vscode://schemas/locale"
},
{
"fileMatch": "/.sqlops/settings.json",
"url": "vscode://schemas/settings"
"fileMatch": "/.vscode/settings.json",
"url": "vscode://schemas/settings/folder"
},
{
"fileMatch": "/.sqlops/launch.json",
"fileMatch": "/.vscode/launch.json",
"url": "vscode://schemas/launch"
},
{
"fileMatch": "/.sqlops/tasks.json",
"fileMatch": "/.vscode/tasks.json",
"url": "vscode://schemas/tasks"
},
{
@@ -82,6 +74,6 @@
]
},
"devDependencies": {
"@types/node": "^7.0.4"
"@types/node": "7.0.4"
}
}
}
@@ -27,8 +27,8 @@ export function activate(context): void {
//settings.json suggestions
context.subscriptions.push(registerSettingsCompletions());
//extensions.json suggestions
context.subscriptions.push(registerExtensionsCompletions());
//extensions suggestions
context.subscriptions.push(...registerExtensionsCompletions());
// launch.json decorations
context.subscriptions.push(vscode.window.onDidChangeActiveTextEditor(editor => updateLaunchJsonDecorations(editor), null, context.subscriptions));
@@ -67,43 +67,69 @@ function registerSettingsCompletions(): vscode.Disposable {
});
}
function registerExtensionsCompletions(): vscode.Disposable {
interface IExtensionsContent {
recommendations: string[];
}
function registerExtensionsCompletions(): vscode.Disposable[] {
return [registerExtensionsCompletionsInExtensionsDocument(), registerExtensionsCompletionsInWorkspaceConfigurationDocument()];
}
function registerExtensionsCompletionsInExtensionsDocument(): 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];
}
}
const extensionsContent = <IExtensionsContent>parse(document.getText());
return provideInstalledExtensionProposals(extensionsContent, range);
}
return [];
}
});
}
function registerExtensionsCompletionsInWorkspaceConfigurationDocument(): vscode.Disposable {
return vscode.languages.registerCompletionItemProvider({ pattern: '**/*.code-workspace' }, {
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] === 'extensions' && location.path[1] === 'recommendations') {
const extensionsContent = <IExtensionsContent>parse(document.getText())['extensions'];
return provideInstalledExtensionProposals(extensionsContent, range);
}
return [];
}
});
}
function provideInstalledExtensionProposals(extensionsContent: IExtensionsContent, range: vscode.Range): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList> {
const alreadyEnteredExtensions = extensionsContent && extensionsContent.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];
}
}
}
function newSimpleCompletionItem(label: string, range: vscode.Range, description?: string, insertText?: string): vscode.CompletionItem {
const item = new vscode.CompletionItem(label);
item.kind = vscode.CompletionItemKind.Value;
@@ -43,13 +43,13 @@ export class SettingsDocument {
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('${activeEditorShort}', range, localize('activeEditorShort', "the file name (e.g. myFile.txt)")));
completions.push(this.newSimpleCompletionItem('${activeEditorMedium}', range, localize('activeEditorMedium', "the path of the file relative to the workspace folder (e.g. myFolder/myFile.txt)")));
completions.push(this.newSimpleCompletionItem('${activeEditorLong}', range, localize('activeEditorLong', "the full path of the file (e.g. /Users/Development/myProject/myFolder/myFile.txt)")));
completions.push(this.newSimpleCompletionItem('${rootName}', range, localize('rootName', "name of the workspace (e.g. myFolder or myWorkspace)")));
completions.push(this.newSimpleCompletionItem('${rootPath}', range, localize('rootPath', "file path of the workspace (e.g. /Users/Development/myWorkspace)")));
completions.push(this.newSimpleCompletionItem('${folderName}', range, localize('folderName', "name of the workspace folder the file is contained in (e.g. myFolder)")));
completions.push(this.newSimpleCompletionItem('${folderPath}', range, localize('folderPath', "file path of the workspace folder the file is contained in (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")));
+2 -1
View File
@@ -5,7 +5,8 @@
"@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"
"resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.78.tgz",
"dev": true
},
"argparse": {
"version": "1.0.9",
+1 -1
View File
@@ -47,6 +47,6 @@
},
"devDependencies": {
"@types/markdown-it": "0.0.2",
"@types/node": "^7.0.4"
"@types/node": "7.0.43"
}
}
@@ -46,7 +46,7 @@ const _linkProvider = new class implements vscode.DocumentLinkProvider {
const offset = lookUp(match[1]);
if (offset === -1) {
console.warn(match[1]);
console.warn(`Could not find symbol for link ${match[1]}`);
continue;
}
@@ -8,8 +8,7 @@ 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 * as MarkdownItType from 'markdown-it';
import { languages, workspace, Disposable, ExtensionContext, TextDocument, Uri, Diagnostic, Range, DiagnosticSeverity, Position } from 'vscode';
@@ -33,7 +32,7 @@ enum Context {
}
interface TokenAndPosition {
token: MarkdownIt.Token;
token: MarkdownItType.Token;
begin: number;
end: number;
}
@@ -53,7 +52,7 @@ export class ExtensionLinter {
private packageJsonQ = new Set<TextDocument>();
private readmeQ = new Set<TextDocument>();
private timer: NodeJS.Timer;
private markdownIt = new MarkdownIt();
private markdownIt: MarkdownItType.MarkdownIt;
constructor(private context: ExtensionContext) {
this.disposables.push(
@@ -148,8 +147,11 @@ export class ExtensionLinter {
}
const text = document.getText();
if (!this.markdownIt) {
this.markdownIt = new (await import('markdown-it'));
}
const tokens = this.markdownIt.parse(text, {});
const tokensAndPositions = (function toTokensAndPositions(this: ExtensionLinter, tokens: MarkdownIt.Token[], begin = 0, end = text.length): TokenAndPosition[] {
const tokensAndPositions = (function toTokensAndPositions(this: ExtensionLinter, tokens: MarkdownItType.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));
@@ -192,8 +194,9 @@ export class ExtensionLinter {
});
let svgStart: Diagnostic;
tokensAndPositions.filter(tnp => tnp.token.type === 'text' && tnp.token.content)
.map(tnp => {
for (const tnp of tokensAndPositions) {
if (tnp.token.type === 'text' && tnp.token.content) {
const parse5 = await import('parse5');
const parser = new parse5.SAXParser({ locationInfo: true });
parser.on('startTag', (name, attrs, selfClosing, location) => {
if (name === 'img') {
@@ -220,13 +223,14 @@ export class ExtensionLinter {
});
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) {
private locateToken(text: string, begin: number, end: number, token: MarkdownItType.Token, content: string) {
if (content) {
const tokenBegin = text.indexOf(content, begin);
if (tokenBegin !== -1) {
+6 -1
View File
@@ -7,13 +7,18 @@
"from": "applicationinsights@0.18.0",
"resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-0.18.0.tgz"
},
"byline": {
"version": "5.0.0",
"from": "byline@latest",
"resolved": "https://registry.npmjs.org/byline/-/byline-5.0.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",
"version": "0.0.8",
"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"
},
+110 -4
View File
@@ -36,6 +36,11 @@
"dark": "resources/icons/dark/git.svg"
}
},
{
"command": "git.close",
"title": "%command.close%",
"category": "Git"
},
{
"command": "git.refresh",
"title": "%command.refresh%",
@@ -96,6 +101,24 @@
"title": "%command.revertSelectedRanges%",
"category": "Git"
},
{
"command": "git.stageChange",
"title": "%command.stageChange%",
"category": "Git",
"icon": {
"light": "resources/icons/light/stage.svg",
"dark": "resources/icons/dark/stage.svg"
}
},
{
"command": "git.revertChange",
"title": "%command.revertChange%",
"category": "Git",
"icon": {
"light": "resources/icons/light/clean.svg",
"dark": "resources/icons/dark/clean.svg"
}
},
{
"command": "git.unstage",
"title": "%command.unstage%",
@@ -310,10 +333,18 @@
"command": "git.stageSelectedRanges",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.stageChange",
"when": "false"
},
{
"command": "git.revertSelectedRanges",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.revertChange",
"when": "false"
},
{
"command": "git.unstage",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
@@ -529,6 +560,11 @@
"group": "3_commit",
"when": "config.git.enabled && scmProvider == git"
},
{
"command": "git.stageAll",
"group": "4_stage",
"when": "config.git.enabled && scmProvider == git"
},
{
"command": "git.unstageAll",
"group": "4_stage",
@@ -560,6 +596,13 @@
"when": "config.git.enabled && scmProvider == git"
}
],
"scm/sourceControl": [
{
"command": "git.close",
"group": "navigation",
"when": "config.git.enabled && scmProvider == git"
}
],
"scm/resourceGroup/context": [
{
"command": "git.stageAll",
@@ -688,7 +731,7 @@
{
"command": "git.openChange",
"group": "navigation",
"when": "config.git.enabled && gitOpenRepositoryCount != 0 && !isInDiffEditor && resourceScheme != extension"
"when": "config.git.enabled && gitOpenRepositoryCount != 0 && !isInDiffEditor && resourceScheme == file"
},
{
"command": "git.stageSelectedRanges",
@@ -705,6 +748,16 @@
"group": "2_git@3",
"when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme != merge-conflict.conflict-diff"
}
],
"scm/change/title": [
{
"command": "git.stageChange",
"when": "config.git.enabled && originalResourceScheme == git"
},
{
"command": "git.revertChange",
"when": "config.git.enabled && originalResourceScheme == git"
}
]
},
"configuration": {
@@ -784,18 +837,71 @@
"type": "boolean",
"description": "%config.enableCommitSigning%",
"default": false
},
"git.decorations.enabled": {
"type": "boolean",
"default": true,
"description": "%config.decorations.enabled%"
}
}
}
},
"colors": [
{
"id": "gitDecoration.modifiedResourceForeground",
"description": "%colors.modified%",
"defaults": {
"light": "#a76e12",
"dark": "#E2C08D",
"highContrast": "#E2C08D"
}
},
{
"id": "gitDecoration.deletedResourceForeground",
"description": "%colors.deleted%",
"defaults": {
"light": "#ad0707",
"dark": "#c74e39",
"highContrast": "#c74e39"
}
},
{
"id": "gitDecoration.untrackedResourceForeground",
"description": "%colors.untracked%",
"defaults": {
"light": "#019001",
"dark": "#73C991",
"highContrast": "#73C991"
}
},
{
"id": "gitDecoration.ignoredResourceForeground",
"description": "%colors.ignored%",
"defaults": {
"light": "#8E8E90",
"dark": "#A7A8A9",
"highContrast": "#A7A8A9"
}
},
{
"id": "gitDecoration.conflictingResourceForeground",
"description": "%colors.conflict%",
"defaults": {
"light": "#6c6cc4",
"dark": "#6c6cc4",
"highContrast": "#6c6cc4"
}
}
]
},
"dependencies": {
"byline": "^5.0.0",
"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",
"@types/mocha": "2.2.43",
"@types/node": "7.0.43",
"mocha": "^3.2.0"
}
}
+10 -2
View File
@@ -10,6 +10,8 @@
"command.stageAll": "Stage All Changes",
"command.stageSelectedRanges": "Stage Selected Ranges",
"command.revertSelectedRanges": "Revert Selected Ranges",
"command.stageChange": "Stage Change",
"command.revertChange": "Revert Change",
"command.unstage": "Unstage Changes",
"command.unstageAll": "Unstage All Changes",
"command.unstageSelectedRanges": "Unstage Selected Ranges",
@@ -54,5 +56,11 @@
"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."
}
"config.discardAllScope": "Controls what changes are discarded by the `Discard all changes` command. `all` discards all changes. `tracked` discards only tracked files. `prompt` shows a prompt dialog every time the action is run.",
"config.decorations.enabled": "Controls if Git contributes colors and badges to the explorer and the open editors view.",
"colors.modified": "Color for modified resources.",
"colors.deleted": "Color for deleted resources.",
"colors.untracked": "Color for untracked resources.",
"colors.ignored": "Color for ignored resources.",
"colors.conflict": "Color for resources with conflicts."
}
+36 -14
View File
@@ -5,16 +5,23 @@
'use strict';
import { workspace, Disposable } from 'vscode';
import { workspace, Disposable, EventEmitter } from 'vscode';
import { GitErrorCodes } from './git';
import { Repository } from './repository';
import { throttle } from './decorators';
import { eventToPromise, filterEvent } from './util';
export class AutoFetcher {
private static Period = 3 * 60 * 1000 /* three minutes */;
private _onDidChange = new EventEmitter<boolean>();
private onDidChange = this._onDidChange.event;
private _enabled: boolean = false;
get enabled(): boolean { return this._enabled; }
set enabled(enabled: boolean) { this._enabled = enabled; this._onDidChange.fire(enabled); }
private disposables: Disposable[] = [];
private timer: NodeJS.Timer;
constructor(private repository: Repository) {
workspace.onDidChangeConfiguration(this.onConfiguration, this, this.disposables);
@@ -32,26 +39,41 @@ export class AutoFetcher {
}
enable(): void {
if (this.timer) {
if (this.enabled) {
return;
}
this.fetch();
this.timer = setInterval(() => this.fetch(), AutoFetcher.Period);
this.enabled = true;
this.run();
}
disable(): void {
clearInterval(this.timer);
this.enabled = false;
}
@throttle
private async fetch(): Promise<void> {
try {
await this.repository.fetch();
} catch (err) {
if (err.gitErrorCode === GitErrorCodes.AuthenticationFailed) {
this.disable();
private async run(): Promise<void> {
while (this.enabled) {
await this.repository.whenIdleAndFocused();
if (!this.enabled) {
return;
}
try {
await this.repository.fetch();
} catch (err) {
if (err.gitErrorCode === GitErrorCodes.AuthenticationFailed) {
this.disable();
}
}
if (!this.enabled) {
return;
}
const timeout = new Promise(c => setTimeout(c, AutoFetcher.Period));
const whenDisabled = eventToPromise(filterEvent(this.onDidChange, enabled => !enabled));
await Promise.race([timeout, whenDisabled]);
}
}
+183 -69
View File
@@ -5,11 +5,12 @@
'use strict';
import { Uri, commands, Disposable, window, workspace, QuickPickItem, OutputChannel, Range, WorkspaceEdit, Position, LineChange, SourceControlResourceState, TextDocumentShowOptions, ViewColumn, ProgressLocation } from 'vscode';
import { Uri, commands, Disposable, window, workspace, QuickPickItem, OutputChannel, Range, WorkspaceEdit, Position, LineChange, SourceControlResourceState, TextDocumentShowOptions, ViewColumn, ProgressLocation, TextEditor } from 'vscode';
import { Ref, RefType, Git, GitErrorCodes, Branch } from './git';
import { Repository, Resource, Status, CommitOptions, ResourceGroupType } from './repository';
import { Model } from './model';
import { toGitUri, fromGitUri } from './uri';
import { grep } from './util';
import { applyLineChanges, intersectDiffWithRange, toLineRanges, invertLineChange } from './staging';
import * as path from 'path';
import * as os from 'os';
@@ -92,11 +93,13 @@ class MergeItem implements QuickPickItem {
class CreateBranchItem implements QuickPickItem {
constructor(private cc: CommandCenter) { }
get label(): string { return localize('create branch', '$(plus) Create new branch'); }
get description(): string { return ''; }
async run(repository: Repository): Promise<void> {
await commands.executeCommand('git.branch');
await this.cc.branch(repository);
}
}
@@ -169,12 +172,14 @@ export class CommandCenter {
const opts: TextDocumentShowOptions = {
preserveFocus,
preview,
viewColumn: window.activeTextEditor && window.activeTextEditor.viewColumn || ViewColumn.One
viewColumn: ViewColumn.Active
};
const activeTextEditor = window.activeTextEditor;
if (preserveSelection && activeTextEditor && activeTextEditor.document.uri.fsPath === right.fsPath) {
// Check if active text editor has same path as other editor. we cannot compare via
// URI.toString() here because the schemas can be different. Instead we just go by path.
if (preserveSelection && activeTextEditor && activeTextEditor.document.uri.path === right.path) {
opts.selection = activeTextEditor.selection;
}
@@ -257,13 +262,20 @@ export class CommandCenter {
}
@command('git.clone')
async clone(): Promise<void> {
const url = await window.showInputBox({
prompt: localize('repourl', "Repository URL"),
ignoreFocusOut: true
});
async clone(url?: string): Promise<void> {
if (!url) {
url = await window.showInputBox({
prompt: localize('repourl', "Repository URL"),
ignoreFocusOut: true
});
}
if (!url) {
/* __GDPR__
"clone" : {
"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'no_URL' });
return;
}
@@ -278,6 +290,11 @@ export class CommandCenter {
});
if (!parentPath) {
/* __GDPR__
"clone" : {
"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'no_directory' });
return;
}
@@ -295,14 +312,30 @@ export class CommandCenter {
const result = await window.showInformationMessage(localize('proposeopen', "Would you like to open the cloned repository?"), open);
const openFolder = result === open;
/* __GDPR__
"clone" : {
"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"openFolder": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }
}
*/
this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'success' }, { openFolder: openFolder ? 1 : 0 });
if (openFolder) {
commands.executeCommand('vscode.openFolder', Uri.file(repositoryPath));
}
} catch (err) {
if (/already exists and is not an empty directory/.test(err && err.stderr || '')) {
/* __GDPR__
"clone" : {
"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'directory_not_empty' });
} else {
/* __GDPR__
"clone" : {
"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'error' });
}
throw err;
@@ -311,25 +344,44 @@ export class CommandCenter {
@command('git.init')
async init(): Promise<void> {
const value = workspace.workspaceFolders && workspace.workspaceFolders.length > 0
? workspace.workspaceFolders[0].uri.fsPath
: os.homedir();
const homeUri = Uri.file(os.homedir());
const defaultUri = workspace.workspaceFolders && workspace.workspaceFolders.length > 0
? Uri.file(workspace.workspaceFolders[0].uri.fsPath)
: homeUri;
const path = await window.showInputBox({
placeHolder: localize('path to init', "Folder path"),
prompt: localize('provide path', "Please provide a folder path to initialize a Git repository"),
value,
ignoreFocusOut: true
const result = await window.showOpenDialog({
canSelectFiles: false,
canSelectFolders: true,
canSelectMany: false,
defaultUri,
openLabel: localize('init repo', "Initialize Repository")
});
if (!path) {
if (!result || result.length === 0) {
return;
}
const uri = result[0];
if (homeUri.toString().startsWith(uri.toString())) {
const yes = localize('create repo', "Initialize Repository");
const answer = await window.showWarningMessage(localize('are you sure', "This will create a Git repository in '{0}'. Are you sure you want to continue?", uri.fsPath), yes);
if (answer !== yes) {
return;
}
}
const path = uri.fsPath;
await this.git.init(path);
await this.model.tryOpenRepository(path);
}
@command('git.close', { repository: true })
async close(repository: Repository): Promise<void> {
this.model.close(repository);
}
@command('git.openFile')
async openFile(arg?: Resource | Uri, ...resourceStates: SourceControlResourceState[]): Promise<void> {
const preserveFocus = arg instanceof Resource;
@@ -364,11 +416,13 @@ export class CommandCenter {
for (const uri of uris) {
const opts: TextDocumentShowOptions = {
preserveFocus,
preview: preview,
viewColumn: activeTextEditor && activeTextEditor.viewColumn || ViewColumn.One
preview,
viewColumn: ViewColumn.Active
};
if (activeTextEditor && activeTextEditor.document.uri.fsPath === uri.fsPath) {
// Check if active text editor has same path as other editor. we cannot compare via
// URI.toString() here because the schemas can be different. Instead we just go by path.
if (activeTextEditor && activeTextEditor.document.uri.path === uri.path) {
opts.selection = activeTextEditor.selection;
}
@@ -451,12 +505,20 @@ export class CommandCenter {
}
const selection = resourceStates.filter(s => s instanceof Resource) as Resource[];
const mergeConflicts = selection.filter(s => s.resourceGroupType === ResourceGroupType.Merge);
const merge = selection.filter(s => s.resourceGroupType === ResourceGroupType.Merge);
const bothModified = merge.filter(s => s.type === Status.BOTH_MODIFIED);
const promises = bothModified.map(s => grep(s.resourceUri.fsPath, /^<{7}|^={7}|^>{7}/));
const unresolvedBothModified = await Promise.all<boolean>(promises);
const resolvedConflicts = bothModified.filter((s, i) => !unresolvedBothModified[i]);
const unresolvedConflicts = [
...merge.filter(s => s.type !== Status.BOTH_MODIFIED),
...bothModified.filter((s, i) => unresolvedBothModified[i])
];
if (mergeConflicts.length > 0) {
const message = mergeConflicts.length > 1
? localize('confirm stage files with merge conflicts', "Are you sure you want to stage {0} files with merge conflicts?", mergeConflicts.length)
: localize('confirm stage file with merge conflicts', "Are you sure you want to stage {0} with merge conflicts?", path.basename(mergeConflicts[0].resourceUri.fsPath));
if (unresolvedConflicts.length > 0) {
const message = unresolvedConflicts.length > 1
? localize('confirm stage files with merge conflicts', "Are you sure you want to stage {0} files with merge conflicts?", unresolvedConflicts.length)
: localize('confirm stage file with merge conflicts', "Are you sure you want to stage {0} with merge conflicts?", path.basename(unresolvedConflicts[0].resourceUri.fsPath));
const yes = localize('yes', "Yes");
const pick = await window.showWarningMessage(message, { modal: true }, yes);
@@ -466,10 +528,8 @@ export class CommandCenter {
}
}
const workingTree = selection
.filter(s => s.resourceGroupType === ResourceGroupType.WorkingTree);
const scmResources = [...workingTree, ...mergeConflicts];
const workingTree = selection.filter(s => s.resourceGroupType === ResourceGroupType.WorkingTree);
const scmResources = [...workingTree, ...resolvedConflicts, ...unresolvedConflicts];
if (!scmResources.length) {
return;
@@ -500,14 +560,39 @@ export class CommandCenter {
await repository.add([]);
}
@command('git.stageChange')
async stageChange(uri: Uri, changes: LineChange[], index: number): Promise<void> {
const textEditor = window.visibleTextEditors.filter(e => e.document.uri.toString() === uri.toString())[0];
if (!textEditor) {
return;
}
await this._stageChanges(textEditor, [changes[index]]);
}
@command('git.stageSelectedRanges', { diff: true })
async stageSelectedRanges(diffs: LineChange[]): Promise<void> {
async stageSelectedChanges(changes: LineChange[]): Promise<void> {
const textEditor = window.activeTextEditor;
if (!textEditor) {
return;
}
const modifiedDocument = textEditor.document;
const selectedLines = toLineRanges(textEditor.selections, modifiedDocument);
const selectedChanges = changes
.map(diff => selectedLines.reduce<LineChange | null>((result, range) => result || intersectDiffWithRange(modifiedDocument, diff, range), null))
.filter(d => !!d) as LineChange[];
if (!selectedChanges.length) {
return;
}
await this._stageChanges(textEditor, selectedChanges);
}
private async _stageChanges(textEditor: TextEditor, changes: LineChange[]): Promise<void> {
const modifiedDocument = textEditor.document;
const modifiedUri = modifiedDocument.uri;
@@ -517,28 +602,48 @@ export class CommandCenter {
const originalUri = toGitUri(modifiedUri, '~');
const originalDocument = await workspace.openTextDocument(originalUri);
const selectedLines = toLineRanges(textEditor.selections, modifiedDocument);
const selectedDiffs = diffs
.map(diff => selectedLines.reduce<LineChange | null>((result, range) => result || intersectDiffWithRange(modifiedDocument, diff, range), null))
.filter(d => !!d) as LineChange[];
if (!selectedDiffs.length) {
return;
}
const result = applyLineChanges(originalDocument, modifiedDocument, selectedDiffs);
const result = applyLineChanges(originalDocument, modifiedDocument, changes);
await this.runByRepository(modifiedUri, async (repository, resource) => await repository.stage(resource, result));
}
@command('git.revertChange')
async revertChange(uri: Uri, changes: LineChange[], index: number): Promise<void> {
const textEditor = window.visibleTextEditors.filter(e => e.document.uri.toString() === uri.toString())[0];
if (!textEditor) {
return;
}
await this._revertChanges(textEditor, [...changes.slice(0, index), ...changes.slice(index + 1)]);
}
@command('git.revertSelectedRanges', { diff: true })
async revertSelectedRanges(diffs: LineChange[]): Promise<void> {
async revertSelectedRanges(changes: LineChange[]): Promise<void> {
const textEditor = window.activeTextEditor;
if (!textEditor) {
return;
}
const modifiedDocument = textEditor.document;
const selections = textEditor.selections;
const selectedChanges = changes.filter(change => {
const modifiedRange = change.modifiedEndLineNumber === 0
? new Range(modifiedDocument.lineAt(change.modifiedStartLineNumber - 1).range.end, modifiedDocument.lineAt(change.modifiedStartLineNumber).range.start)
: new Range(modifiedDocument.lineAt(change.modifiedStartLineNumber - 1).range.start, modifiedDocument.lineAt(change.modifiedEndLineNumber - 1).range.end);
return selections.every(selection => !selection.intersection(modifiedRange));
});
if (selectedChanges.length === changes.length) {
return;
}
await this._revertChanges(textEditor, selectedChanges);
}
private async _revertChanges(textEditor: TextEditor, changes: LineChange[]): Promise<void> {
const modifiedDocument = textEditor.document;
const modifiedUri = modifiedDocument.uri;
@@ -548,19 +653,6 @@ export class CommandCenter {
const originalUri = toGitUri(modifiedUri, '~');
const originalDocument = await workspace.openTextDocument(originalUri);
const selections = textEditor.selections;
const selectedDiffs = diffs.filter(diff => {
const modifiedRange = diff.modifiedEndLineNumber === 0
? new Range(modifiedDocument.lineAt(diff.modifiedStartLineNumber - 1).range.end, modifiedDocument.lineAt(diff.modifiedStartLineNumber).range.start)
: new Range(modifiedDocument.lineAt(diff.modifiedStartLineNumber - 1).range.start, modifiedDocument.lineAt(diff.modifiedEndLineNumber - 1).range.end);
return selections.every(selection => !selection.intersection(modifiedRange));
});
if (selectedDiffs.length === diffs.length) {
return;
}
const basename = path.basename(modifiedUri.fsPath);
const message = localize('confirm revert', "Are you sure you want to revert the selected changes in {0}?", basename);
const yes = localize('revert', "Revert Changes");
@@ -570,10 +662,11 @@ export class CommandCenter {
return;
}
const result = applyLineChanges(originalDocument, modifiedDocument, selectedDiffs);
const result = applyLineChanges(originalDocument, modifiedDocument, changes);
const edit = new WorkspaceEdit();
edit.replace(modifiedUri, new Range(new Position(0, 0), modifiedDocument.lineAt(modifiedDocument.lineCount - 1).range.end), result);
workspace.applyEdit(edit);
await modifiedDocument.save();
}
@command('git.unstage')
@@ -909,7 +1002,7 @@ export class CommandCenter {
const includeTags = checkoutType === 'all' || checkoutType === 'tags';
const includeRemotes = checkoutType === 'all' || checkoutType === 'remote';
const createBranch = new CreateBranchItem();
const createBranch = new CreateBranchItem(this);
const heads = repository.refs.filter(ref => ref.type === RefType.Head)
.map(ref => new CheckoutItem(ref));
@@ -1174,6 +1267,19 @@ export class CommandCenter {
await repository.sync();
}
@command('git._syncAll')
async syncAll(): Promise<void> {
await Promise.all(this.model.repositories.map(async repository => {
const HEAD = repository.HEAD;
if (!HEAD || !HEAD.upstream) {
return;
}
await repository.sync();
}));
}
@command('git.publish', { repository: true })
async publish(repository: Repository): Promise<void> {
const remotes = repository.remotes;
@@ -1184,9 +1290,12 @@ export class CommandCenter {
}
const branchName = repository.HEAD && repository.HEAD.name || '';
const picks = repository.remotes.map(r => r.name);
const placeHolder = localize('pick remote', "Pick a remote to publish the branch '{0}' to:", branchName);
const choice = await window.showQuickPick(picks, { placeHolder });
const selectRemote = async () => {
const picks = repository.remotes.map(r => r.name);
const placeHolder = localize('pick remote', "Pick a remote to publish the branch '{0}' to:", branchName);
return await window.showQuickPick(picks, { placeHolder });
};
const choice = remotes.length === 1 ? remotes[0].name : await selectRemote();
if (!choice) {
return;
@@ -1200,27 +1309,27 @@ export class CommandCenter {
this.outputChannel.show();
}
@command('git.ignore', { repository: true })
async ignore(repository: Repository, ...resourceStates: SourceControlResourceState[]): Promise<void> {
@command('git.ignore')
async ignore(...resourceStates: SourceControlResourceState[]): Promise<void> {
if (resourceStates.length === 0 || !(resourceStates[0].resourceUri instanceof Uri)) {
const uri = window.activeTextEditor && window.activeTextEditor.document.uri;
const resource = this.getSCMResource();
if (!uri) {
if (!resource) {
return;
}
return await repository.ignore([uri]);
resourceStates = [resource];
}
const uris = resourceStates
const resources = resourceStates
.filter(s => s instanceof Resource)
.map(r => r.resourceUri);
if (!uris.length) {
if (!resources.length) {
return;
}
await repository.ignore(uris);
await this.runByRepository(resources, async (repository, resources) => repository.ignore(resources));
}
@command('git.stash', { repository: true })
@@ -1302,6 +1411,11 @@ export class CommandCenter {
});
}
/* __GDPR__
"git.command" : {
"command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryReporter.sendTelemetryEvent('git.command', { command: id });
return result.catch(async err => {
@@ -1389,7 +1503,7 @@ export class CommandCenter {
return result;
}
const tuple = result.filter(p => p[0] === repository)[0];
const tuple = result.filter(p => p.repository === repository)[0];
if (tuple) {
tuple.resources.push(resource);
+28 -9
View File
@@ -6,9 +6,10 @@
'use strict';
import { workspace, Uri, Disposable, Event, EventEmitter, window } from 'vscode';
import { debounce } from './decorators';
import { fromGitUri } from './uri';
import { Model, ModelChangeEvent } from './model';
import { debounce, throttle } from './decorators';
import { fromGitUri, toGitUri } from './uri';
import { Model, ModelChangeEvent, OriginalResourceChangeEvent } from './model';
import { filterEvent, eventToPromise } from './util';
interface CacheRow {
uri: Uri;
@@ -24,8 +25,8 @@ const FIVE_MINUTES = 1000 * 60 * 5;
export class GitContentProvider {
private onDidChangeEmitter = new EventEmitter<Uri>();
get onDidChange(): Event<Uri> { return this.onDidChangeEmitter.event; }
private _onDidChange = new EventEmitter<Uri>();
get onDidChange(): Event<Uri> { return this._onDidChange.event; }
private changedRepositoryRoots = new Set<string>();
private cache: Cache = Object.create(null);
@@ -34,6 +35,7 @@ export class GitContentProvider {
constructor(private model: Model) {
this.disposables.push(
model.onDidChangeRepository(this.onDidChangeRepository, this),
model.onDidChangeOriginalResource(this.onDidChangeOriginalResource, this),
workspace.registerTextDocumentContentProvider('git', this)
);
@@ -45,19 +47,33 @@ export class GitContentProvider {
this.eventuallyFireChangeEvents();
}
private onDidChangeOriginalResource({ uri }: OriginalResourceChangeEvent): void {
if (uri.scheme !== 'file') {
return;
}
this._onDidChange.fire(toGitUri(uri, '', true));
}
@debounce(1100)
private eventuallyFireChangeEvents(): void {
this.fireChangeEvents();
}
private fireChangeEvents(): void {
@throttle
private async fireChangeEvents(): Promise<void> {
if (!window.state.focused) {
const onDidFocusWindow = filterEvent(window.onDidChangeWindowState, e => e.focused);
await eventToPromise(onDidFocusWindow);
}
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);
this._onDidChange.fire(uri);
return;
}
}
@@ -75,7 +91,7 @@ export class GitContentProvider {
const cacheKey = uri.toString();
const timestamp = new Date().getTime();
const cacheValue = { uri, timestamp };
const cacheValue: CacheRow = { uri, timestamp };
this.cache[cacheKey] = cacheValue;
@@ -101,7 +117,10 @@ export class GitContentProvider {
Object.keys(this.cache).forEach(key => {
const row = this.cache[key];
const isOpen = window.visibleTextEditors.some(e => e.document.uri.fsPath === row.uri.fsPath);
const { path } = fromGitUri(row.uri);
const isOpen = workspace.textDocuments
.filter(d => d.uri.scheme === 'file')
.some(d => d.uri.fsPath === path);
if (isOpen || now - row.timestamp < THREE_MINUTES) {
cache[row.uri.toString()] = row;
+173
View File
@@ -0,0 +1,173 @@
/*---------------------------------------------------------------------------------------------
* 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 { window, workspace, Uri, Disposable, Event, EventEmitter, DecorationData, DecorationProvider, ThemeColor } from 'vscode';
import { Repository, GitResourceGroup } from './repository';
import { Model } from './model';
import { debounce } from './decorators';
import { filterEvent } from './util';
class GitIgnoreDecorationProvider implements DecorationProvider {
private readonly _onDidChangeDecorations = new EventEmitter<Uri[]>();
readonly onDidChangeDecorations: Event<Uri[]> = this._onDidChangeDecorations.event;
private checkIgnoreQueue = new Map<string, { resolve: (status: boolean) => void, reject: (err: any) => void }>();
private disposables: Disposable[] = [];
constructor(private repository: Repository) {
this.disposables.push(
window.registerDecorationProvider(this),
filterEvent(workspace.onDidSaveTextDocument, e => e.fileName.endsWith('.gitignore'))(_ => this._onDidChangeDecorations.fire())
//todo@joh -> events when the ignore status actually changes, not only when the file changes
);
}
dispose(): void {
this.disposables.forEach(d => d.dispose());
this.checkIgnoreQueue.clear();
}
provideDecoration(uri: Uri): Promise<DecorationData | undefined> {
return new Promise<boolean>((resolve, reject) => {
this.checkIgnoreQueue.set(uri.fsPath, { resolve, reject });
this.checkIgnoreSoon();
}).then(ignored => {
if (ignored) {
return <DecorationData>{
priority: 3,
color: new ThemeColor('gitDecoration.ignoredResourceForeground')
};
}
});
}
@debounce(500)
private checkIgnoreSoon(): void {
const queue = new Map(this.checkIgnoreQueue.entries());
this.checkIgnoreQueue.clear();
this.repository.checkIgnore([...queue.keys()]).then(ignoreSet => {
for (const [key, value] of queue.entries()) {
value.resolve(ignoreSet.has(key));
}
}, err => {
console.error(err);
for (const [, value] of queue.entries()) {
value.reject(err);
}
});
}
}
class GitDecorationProvider implements DecorationProvider {
private readonly _onDidChangeDecorations = new EventEmitter<Uri[]>();
readonly onDidChangeDecorations: Event<Uri[]> = this._onDidChangeDecorations.event;
private disposables: Disposable[] = [];
private decorations = new Map<string, DecorationData>();
constructor(private repository: Repository) {
this.disposables.push(
window.registerDecorationProvider(this),
repository.onDidRunOperation(this.onDidRunOperation, this)
);
}
private onDidRunOperation(): void {
let newDecorations = new Map<string, DecorationData>();
this.collectDecorationData(this.repository.indexGroup, newDecorations);
this.collectDecorationData(this.repository.workingTreeGroup, newDecorations);
this.collectDecorationData(this.repository.mergeGroup, newDecorations);
let uris: Uri[] = [];
newDecorations.forEach((value, uriString) => {
if (this.decorations.has(uriString)) {
this.decorations.delete(uriString);
} else {
uris.push(Uri.parse(uriString));
}
});
this.decorations.forEach((value, uriString) => {
uris.push(Uri.parse(uriString));
});
this.decorations = newDecorations;
this._onDidChangeDecorations.fire(uris);
}
private collectDecorationData(group: GitResourceGroup, bucket: Map<string, DecorationData>): void {
group.resourceStates.forEach(r => {
if (r.resourceDecoration) {
bucket.set(r.original.toString(), r.resourceDecoration);
}
});
}
provideDecoration(uri: Uri): DecorationData | undefined {
return this.decorations.get(uri.toString());
}
dispose(): void {
this.disposables.forEach(d => d.dispose());
}
}
export class GitDecorations {
private configListener: Disposable;
private modelListener: Disposable[] = [];
private providers = new Map<Repository, Disposable>();
constructor(private model: Model) {
this.configListener = workspace.onDidChangeConfiguration(e => e.affectsConfiguration('git.decorations.enabled') && this.update());
this.update();
}
private update(): void {
const enabled = workspace.getConfiguration('git').get('decorations.enabled');
if (enabled) {
this.enable();
} else {
this.disable();
}
}
private enable(): void {
this.modelListener = [];
this.model.onDidOpenRepository(this.onDidOpenRepository, this, this.modelListener);
this.model.onDidCloseRepository(this.onDidCloseRepository, this, this.modelListener);
this.model.repositories.forEach(this.onDidOpenRepository, this);
}
private disable(): void {
this.modelListener.forEach(d => d.dispose());
this.providers.forEach(value => value.dispose());
this.providers.clear();
}
private onDidOpenRepository(repository: Repository): void {
const provider = new GitDecorationProvider(repository);
const ignoreProvider = new GitIgnoreDecorationProvider(repository);
this.providers.set(repository, Disposable.from(provider, ignoreProvider));
}
private onDidCloseRepository(repository: Repository): void {
const provider = this.providers.get(repository);
if (provider) {
provider.dispose();
this.providers.delete(repository);
}
}
dispose(): void {
this.configListener.dispose();
this.modelListener.forEach(d => d.dispose());
this.providers.forEach(value => value.dispose);
this.providers.clear();
}
}
+47 -41
View File
@@ -13,7 +13,6 @@ import { EventEmitter } from 'events';
import iconv = require('iconv-lite');
import { assign, uniqBy, groupBy, denodeify, IDisposable, toDisposable, dispose, mkdirp } from './util';
const readdir = denodeify<string[]>(fs.readdir);
const readfile = denodeify<string>(fs.readFile);
export interface IGit {
@@ -66,7 +65,7 @@ function findSpecificGit(path: string): Promise<IGit> {
const buffers: Buffer[] = [];
const child = cp.spawn(path, ['--version']);
child.stdout.on('data', (b: Buffer) => buffers.push(b));
child.on('error', e);
child.on('error', cpErrorHandler(e));
child.on('exit', code => code ? e(new Error('Not found')) : c({ path, version: parseVersion(Buffer.concat(buffers).toString('utf8').trim()) }));
});
}
@@ -82,12 +81,12 @@ function findGitDarwin(): Promise<IGit> {
function getVersion(path: string) {
// make sure git executes
cp.exec('git --version', (err, stdout: Buffer) => {
cp.exec('git --version', (err, stdout) => {
if (err) {
return e('git not found');
}
return c({ path, version: parseVersion(stdout.toString('utf8').trim()) });
return c({ path, version: parseVersion(stdout.trim()) });
});
}
@@ -118,48 +117,54 @@ function findSystemGitWin32(base: string): Promise<IGit> {
return findSpecificGit(path.join(base, 'Git', 'cmd', 'git.exe'));
}
function findGitHubGitWin32(): Promise<IGit> {
const github = path.join(process.env['LOCALAPPDATA'], 'GitHub');
return readdir(github).then(children => {
const git = children.filter(child => /^PortableGit/.test(child))[0];
if (!git) {
return Promise.reject<IGit>('Not found');
}
return findSpecificGit(path.join(github, git, 'cmd', 'git.exe'));
});
}
function findGitWin32(): Promise<IGit> {
return findSystemGitWin32(process.env['ProgramW6432'])
.then(void 0, () => findSystemGitWin32(process.env['ProgramFiles(x86)']))
.then(void 0, () => findSystemGitWin32(process.env['ProgramFiles']))
.then(void 0, () => findSpecificGit('git'))
.then(void 0, () => findGitHubGitWin32());
.then(void 0, () => findSpecificGit('git'));
}
export function findGit(hint: string | undefined): Promise<IGit> {
var first = hint ? findSpecificGit(hint) : Promise.reject<IGit>(null);
return first.then(void 0, () => {
switch (process.platform) {
case 'darwin': return findGitDarwin();
case 'win32': return findGitWin32();
default: return findSpecificGit('git');
}
});
return first
.then(void 0, () => {
switch (process.platform) {
case 'darwin': return findGitDarwin();
case 'win32': return findGitWin32();
default: return findSpecificGit('git');
}
})
.then(null, () => Promise.reject(new Error('Git installation not found.')));
}
export interface IExecutionResult {
exitCode: number;
stdout: string;
stderr: string;
}
async function exec(child: cp.ChildProcess, options: any = {}): Promise<IExecutionResult> {
function cpErrorHandler(cb: (reason?: any) => void): (reason?: any) => void {
return err => {
if (/ENOENT/.test(err.message)) {
err = new GitError({
error: err,
message: 'Failed to execute git (ENOENT)',
gitErrorCode: GitErrorCodes.NotAGitRepository
});
}
cb(err);
};
}
export interface SpawnOptions extends cp.SpawnOptions {
input?: string;
encoding?: string;
log?: boolean;
}
async function exec(child: cp.ChildProcess, options: SpawnOptions = {}): Promise<IExecutionResult> {
if (!child.stdout || !child.stderr) {
throw new GitError({
message: 'Failed to get stdout or stderr from git process.'
@@ -183,7 +188,7 @@ async function exec(child: cp.ChildProcess, options: any = {}): Promise<IExecuti
const [exitCode, stdout, stderr] = await Promise.all<any>([
new Promise<number>((c, e) => {
once(child, 'error', e);
once(child, 'error', cpErrorHandler(e));
once(child, 'exit', c);
}),
new Promise<string>(c => {
@@ -246,7 +251,7 @@ export class GitError {
gitCommand: this.gitCommand,
stdout: this.stdout,
stderr: this.stderr
}, [], 2);
}, null, 2);
if (this.error) {
result += (<any>this.error).stack;
@@ -350,17 +355,17 @@ export class Git {
return path.normalize(result.stdout.trim());
}
async exec(cwd: string, args: string[], options: any = {}): Promise<IExecutionResult> {
async exec(cwd: string, args: string[], options: SpawnOptions = {}): Promise<IExecutionResult> {
options = assign({ cwd }, options || {});
return await this._exec(args, options);
}
stream(cwd: string, args: string[], options: any = {}): cp.ChildProcess {
stream(cwd: string, args: string[], options: SpawnOptions = {}): cp.ChildProcess {
options = assign({ cwd }, options || {});
return this.spawn(args, options);
}
private async _exec(args: string[], options: any = {}): Promise<IExecutionResult> {
private async _exec(args: string[], options: SpawnOptions = {}): Promise<IExecutionResult> {
const child = this.spawn(args, options);
if (options.input) {
@@ -387,7 +392,7 @@ export class Git {
return result;
}
spawn(args: string[], options: any = {}): cp.ChildProcess {
spawn(args: string[], options: SpawnOptions = {}): cp.ChildProcess {
if (!this.gitPath) {
throw new Error('git could not be found in the system.');
}
@@ -505,19 +510,19 @@ export class Repository {
}
// TODO@Joao: rename to exec
async run(args: string[], options: any = {}): Promise<IExecutionResult> {
async run(args: string[], options: SpawnOptions = {}): Promise<IExecutionResult> {
return await this.git.exec(this.repositoryRoot, args, options);
}
stream(args: string[], options: any = {}): cp.ChildProcess {
stream(args: string[], options: SpawnOptions = {}): cp.ChildProcess {
return this.git.stream(this.repositoryRoot, args, options);
}
spawn(args: string[], options: any = {}): cp.ChildProcess {
spawn(args: string[], options: SpawnOptions = {}): cp.ChildProcess {
return this.git.spawn(args, options);
}
async config(scope: string, key: string, value: any, options: any): Promise<string> {
async config(scope: string, key: string, value: any, options: SpawnOptions): Promise<string> {
const args = ['config'];
if (scope) {
@@ -883,7 +888,8 @@ export class Repository {
getStatus(limit = 5000): Promise<{ status: IFileStatus[]; didHitLimit: boolean; }> {
return new Promise<{ status: IFileStatus[]; didHitLimit: boolean; }>((c, e) => {
const parser = new GitStatusParser();
const child = this.stream(['status', '-z', '-u']);
const env = { GIT_OPTIONAL_LOCKS: '0' };
const child = this.stream(['status', '-z', '-u'], { env });
const onExit = exitCode => {
if (exitCode !== 0) {
@@ -919,7 +925,7 @@ export class Repository {
child.stderr.setEncoding('utf8');
child.stderr.on('data', raw => stderrData.push(raw as string));
child.on('error', e);
child.on('error', cpErrorHandler(e));
child.on('exit', onExit);
});
}
+4 -2
View File
@@ -12,6 +12,7 @@ import { findGit, Git, IGit } from './git';
import { Model } from './model';
import { CommandCenter } from './commands';
import { GitContentProvider } from './contentProvider';
import { GitDecorations } from './decorationProvider';
import { Askpass } from './askpass';
import { toDisposable } from './util';
import TelemetryReporter from 'vscode-extension-telemetry';
@@ -54,6 +55,7 @@ async function init(context: ExtensionContext, disposables: Disposable[]): Promi
disposables.push(
new CommandCenter(git, model, outputChannel, telemetryReporter),
new GitContentProvider(model),
new GitDecorations(model)
);
await checkGitVersion(info);
@@ -70,7 +72,7 @@ export function activate(context: ExtensionContext): any {
async function checkGitVersion(info: IGit): Promise<void> {
// {{SQL CARBON EDIT}}
// remove Git version check on since for Carbon
// remove Git version check for sqlops
return;
@@ -101,4 +103,4 @@ async function checkGitVersion(info: IGit): Promise<void> {
await config.update('ignoreLegacyWarning', true, true);
}
*/
}
}
+39 -3
View File
@@ -17,8 +17,16 @@ import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
class RepositoryPick implements QuickPickItem {
@memoize get label(): string { return path.basename(this.repository.root); }
@memoize get description(): string { return path.dirname(this.repository.root); }
@memoize get label(): string {
return path.basename(this.repository.root);
}
@memoize get description(): string {
return [this.repository.headLabel, this.repository.syncLabel]
.filter(l => !!l)
.join(' ');
}
constructor(public readonly repository: Repository) { }
}
@@ -27,10 +35,19 @@ export interface ModelChangeEvent {
uri: Uri;
}
export interface OriginalResourceChangeEvent {
repository: Repository;
uri: Uri;
}
interface OpenRepository extends Disposable {
repository: Repository;
}
function isParent(parent: string, child: string): boolean {
return child.startsWith(parent);
}
export class Model {
private _onDidOpenRepository = new EventEmitter<Repository>();
@@ -42,6 +59,9 @@ export class Model {
private _onDidChangeRepository = new EventEmitter<ModelChangeEvent>();
readonly onDidChangeRepository: Event<ModelChangeEvent> = this._onDidChangeRepository.event;
private _onDidChangeOriginalResource = new EventEmitter<OriginalResourceChangeEvent>();
readonly onDidChangeOriginalResource: Event<OriginalResourceChangeEvent> = this._onDidChangeOriginalResource.event;
private openRepositories: OpenRepository[] = [];
get repositories(): Repository[] { return this.openRepositories.map(r => r.repository); }
@@ -147,7 +167,9 @@ export class Model {
const activeRepositories = new Set<Repository>(activeRepositoriesList);
const openRepositoriesToDispose = removed
.map(folder => this.getOpenRepository(folder.uri))
.filter(r => !!r && !activeRepositories.has(r.repository)) as OpenRepository[];
.filter(r => !!r)
.filter(r => !activeRepositories.has(r!.repository))
.filter(r => !(workspace.workspaceFolders || []).some(f => isParent(f.uri.fsPath, r!.repository.root))) as OpenRepository[];
possibleRepositoryFolders.forEach(p => this.tryOpenRepository(p.uri.fsPath));
openRepositoriesToDispose.forEach(r => r.dispose());
@@ -203,10 +225,14 @@ export class Model {
const onDidDisappearRepository = filterEvent(repository.onDidChangeState, state => state === RepositoryState.Disposed);
const disappearListener = onDidDisappearRepository(() => dispose());
const changeListener = repository.onDidChangeRepository(uri => this._onDidChangeRepository.fire({ repository, uri }));
const originalResourceChangeListener = repository.onDidChangeOriginalResource(uri => this._onDidChangeOriginalResource.fire({ repository, uri }));
const dispose = () => {
disappearListener.dispose();
changeListener.dispose();
originalResourceChangeListener.dispose();
repository.dispose();
this.openRepositories = this.openRepositories.filter(e => e !== openRepository);
this._onDidCloseRepository.fire(repository);
};
@@ -216,6 +242,16 @@ export class Model {
this._onDidOpenRepository.fire(repository);
}
close(repository: Repository): void {
const openRepository = this.getOpenRepository(repository);
if (!openRepository) {
return;
}
openRepository.dispose();
}
async pickRepository(): Promise<Repository | undefined> {
if (this.openRepositories.length === 0) {
throw new Error(localize('no repositories', "There are no available repositories"));
+225 -68
View File
@@ -5,8 +5,8 @@
'use strict';
import { Uri, Command, EventEmitter, Event, scm, SourceControl, SourceControlInputBox, SourceControlResourceGroup, SourceControlResourceState, SourceControlResourceDecorations, Disposable, ProgressLocation, window, workspace, WorkspaceEdit } from 'vscode';
import { Repository as BaseRepository, Ref, Branch, Remote, Commit, GitErrorCodes, Stash } from './git';
import { Uri, Command, EventEmitter, Event, scm, SourceControl, SourceControlInputBox, SourceControlResourceGroup, SourceControlResourceState, SourceControlResourceDecorations, Disposable, ProgressLocation, window, workspace, WorkspaceEdit, ThemeColor, DecorationData } from 'vscode';
import { Repository as BaseRepository, Ref, Branch, Remote, Commit, GitErrorCodes, Stash, RefType, GitError } from './git';
import { anyEvent, filterEvent, eventToPromise, dispose, find } from './util';
import { memoize, throttle, debounce } from './decorators';
import { toGitUri } from './uri';
@@ -171,13 +171,104 @@ export class Resource implements SourceControlResourceState {
}
get decorations(): SourceControlResourceDecorations {
const light = { iconPath: this.getIconPath('light') };
const dark = { iconPath: this.getIconPath('dark') };
// TODO@joh, still requires restart/redraw in the SCM viewlet
const decorations = workspace.getConfiguration().get<boolean>('git.decorations.enabled');
const light = !decorations ? { iconPath: this.getIconPath('light') } : undefined;
const dark = !decorations ? { iconPath: this.getIconPath('dark') } : undefined;
const tooltip = this.tooltip;
const strikeThrough = this.strikeThrough;
const faded = this.faded;
const letter = this.letter;
const color = this.color;
return { strikeThrough, faded, tooltip, light, dark };
return { strikeThrough, faded, tooltip, light, dark, letter, color, source: 'git.resource' /*todo@joh*/ };
}
get letter(): string | undefined {
switch (this.type) {
case Status.INDEX_MODIFIED:
case Status.MODIFIED:
return 'M';
case Status.INDEX_ADDED:
return 'A';
case Status.INDEX_DELETED:
case Status.DELETED:
return 'D';
case Status.INDEX_RENAMED:
return 'R';
case Status.UNTRACKED:
return 'U';
case Status.IGNORED:
return 'I';
case Status.INDEX_COPIED:
case Status.BOTH_DELETED:
case Status.ADDED_BY_US:
case Status.DELETED_BY_THEM:
case Status.ADDED_BY_THEM:
case Status.DELETED_BY_US:
case Status.BOTH_ADDED:
case Status.BOTH_MODIFIED:
return 'C';
default:
return undefined;
}
}
get color(): ThemeColor | undefined {
switch (this.type) {
case Status.INDEX_MODIFIED:
case Status.MODIFIED:
return new ThemeColor('gitDecoration.modifiedResourceForeground');
case Status.INDEX_DELETED:
case Status.DELETED:
return new ThemeColor('gitDecoration.deletedResourceForeground');
case Status.INDEX_ADDED: // todo@joh - special color?
case Status.INDEX_RENAMED: // todo@joh - special color?
case Status.UNTRACKED:
return new ThemeColor('gitDecoration.untrackedResourceForeground');
case Status.IGNORED:
return new ThemeColor('gitDecoration.ignoredResourceForeground');
case Status.INDEX_COPIED:
case Status.BOTH_DELETED:
case Status.ADDED_BY_US:
case Status.DELETED_BY_THEM:
case Status.ADDED_BY_THEM:
case Status.DELETED_BY_US:
case Status.BOTH_ADDED:
case Status.BOTH_MODIFIED:
return new ThemeColor('gitDecoration.conflictingResourceForeground');
default:
return undefined;
}
}
get priority(): number {
switch (this.type) {
case Status.INDEX_MODIFIED:
case Status.MODIFIED:
return 2;
case Status.IGNORED:
return 3;
case Status.INDEX_COPIED:
case Status.BOTH_DELETED:
case Status.ADDED_BY_US:
case Status.DELETED_BY_THEM:
case Status.ADDED_BY_THEM:
case Status.DELETED_BY_US:
case Status.BOTH_ADDED:
case Status.BOTH_MODIFIED:
return 4;
default:
return 1;
}
}
get resourceDecoration(): DecorationData | undefined {
const title = this.tooltip;
const abbreviation = this.letter;
const color = this.color;
const priority = this.priority;
return { bubble: true, source: 'git.resource', title, abbreviation, color, priority };
}
constructor(
@@ -189,54 +280,34 @@ export class Resource implements SourceControlResourceState {
}
export enum Operation {
Status = 1 << 0,
Add = 1 << 1,
RevertFiles = 1 << 2,
Commit = 1 << 3,
Clean = 1 << 4,
Branch = 1 << 5,
Checkout = 1 << 6,
Reset = 1 << 7,
Fetch = 1 << 8,
Pull = 1 << 9,
Push = 1 << 10,
Sync = 1 << 11,
Show = 1 << 12,
Stage = 1 << 13,
GetCommitTemplate = 1 << 14,
DeleteBranch = 1 << 15,
Merge = 1 << 16,
Ignore = 1 << 17,
Tag = 1 << 18,
Stash = 1 << 19
Status = 'Status',
Add = 'Add',
RevertFiles = 'RevertFiles',
Commit = 'Commit',
Clean = 'Clean',
Branch = 'Branch',
Checkout = 'Checkout',
Reset = 'Reset',
Fetch = 'Fetch',
Pull = 'Pull',
Push = 'Push',
Sync = 'Sync',
Show = 'Show',
Stage = 'Stage',
GetCommitTemplate = 'GetCommitTemplate',
DeleteBranch = 'DeleteBranch',
Merge = 'Merge',
Ignore = 'Ignore',
Tag = 'Tag',
Stash = 'Stash',
CheckIgnore = 'CheckIgnore'
}
// function getOperationName(operation: Operation): string {
// switch (operation) {
// case Operation.Status: return 'Status';
// case Operation.Add: return 'Add';
// case Operation.RevertFiles: return 'RevertFiles';
// case Operation.Commit: return 'Commit';
// case Operation.Clean: return 'Clean';
// case Operation.Branch: return 'Branch';
// case Operation.Checkout: return 'Checkout';
// case Operation.Reset: return 'Reset';
// case Operation.Fetch: return 'Fetch';
// case Operation.Pull: return 'Pull';
// case Operation.Push: return 'Push';
// case Operation.Sync: return 'Sync';
// case Operation.Init: return 'Init';
// case Operation.Show: return 'Show';
// case Operation.Stage: return 'Stage';
// case Operation.GetCommitTemplate: return 'GetCommitTemplate';
// default: return 'unknown';
// }
// }
function isReadOnly(operation: Operation): boolean {
switch (operation) {
case Operation.Show:
case Operation.GetCommitTemplate:
case Operation.CheckIgnore:
return true;
default:
return false;
@@ -246,6 +317,7 @@ function isReadOnly(operation: Operation): boolean {
function shouldShowProgress(operation: Operation): boolean {
switch (operation) {
case Operation.Fetch:
case Operation.CheckIgnore:
return false;
default:
return true;
@@ -259,24 +331,36 @@ export interface Operations {
class OperationsImpl implements Operations {
constructor(private readonly operations: number = 0) {
// noop
private operations = new Map<Operation, number>();
start(operation: Operation): void {
this.operations.set(operation, (this.operations.get(operation) || 0) + 1);
}
start(operation: Operation): OperationsImpl {
return new OperationsImpl(this.operations | operation);
}
end(operation: Operation): void {
const count = (this.operations.get(operation) || 0) - 1;
end(operation: Operation): OperationsImpl {
return new OperationsImpl(this.operations & ~operation);
if (count <= 0) {
this.operations.delete(operation);
} else {
this.operations.set(operation, count);
}
}
isRunning(operation: Operation): boolean {
return (this.operations & operation) !== 0;
return this.operations.has(operation);
}
isIdle(): boolean {
return this.operations === 0;
const operations = this.operations.keys();
for (const operation of operations) {
if (!isReadOnly(operation)) {
return false;
}
}
return true;
}
}
@@ -302,6 +386,9 @@ export class Repository implements Disposable {
private _onDidChangeStatus = new EventEmitter<void>();
readonly onDidChangeStatus: Event<void> = this._onDidChangeStatus.event;
private _onDidChangeOriginalResource = new EventEmitter<Uri>();
readonly onDidChangeOriginalResource: Event<Uri> = this._onDidChangeOriginalResource.event;
private _onRunOperation = new EventEmitter<Operation>();
readonly onRunOperation: Event<Operation> = this._onRunOperation.event;
@@ -382,9 +469,7 @@ export class Repository implements Disposable {
const onRelevantGitChange = filterEvent(onRelevantRepositoryChange, uri => /\/\.git\//.test(uri.path));
onRelevantGitChange(this._onDidChangeRepository.fire, this._onDidChangeRepository, this.disposables);
const label = `${path.basename(repository.root)} (Git)`;
this._sourceControl = scm.createSourceControl('git', label);
this._sourceControl = scm.createSourceControl('git', 'Git', Uri.file(repository.root));
this._sourceControl.acceptInputCommand = { command: 'git.commitWithInput', title: localize('commit', "Commit"), arguments: [this._sourceControl] };
this._sourceControl.quickDiffProvider = this;
this.disposables.push(this._sourceControl);
@@ -449,6 +534,7 @@ export class Repository implements Disposable {
async stage(resource: Uri, contents: string): Promise<void> {
const relativePath = path.relative(this.repository.root, resource.fsPath).replace(/\\/g, '/');
await this.run(Operation.Stage, () => this.repository.stage(relativePath, contents));
this._onDidChangeOriginalResource.fire(resource);
}
async revert(resources: Uri[]): Promise<void> {
@@ -534,11 +620,7 @@ export class Repository implements Disposable {
@throttle
async fetch(): Promise<void> {
try {
await this.run(Operation.Fetch, () => this.repository.fetch());
} catch (err) {
// noop
}
await this.run(Operation.Fetch, () => this.repository.fetch());
}
@throttle
@@ -584,7 +666,7 @@ export class Repository implements Disposable {
async show(ref: string, filePath: string): Promise<string> {
return await this.run(Operation.Show, async () => {
const relativePath = path.relative(this.repository.root, filePath).replace(/\\/g, '/');
const configFiles = workspace.getConfiguration('files');
const configFiles = workspace.getConfiguration('files', Uri.file(filePath));
const encoding = configFiles.get<string>('encoding');
return await this.repository.buffer(`${ref}:${relativePath}`, encoding);
@@ -629,13 +711,58 @@ export class Repository implements Disposable {
});
}
checkIgnore(filePaths: string[]): Promise<Set<string>> {
return this.run(Operation.CheckIgnore, () => {
return new Promise<Set<string>>((resolve, reject) => {
filePaths = filePaths.filter(filePath => !path.relative(this.root, filePath).startsWith('..'));
if (filePaths.length === 0) {
// nothing left
return resolve(new Set<string>());
}
// https://git-scm.com/docs/git-check-ignore#git-check-ignore--z
const child = this.repository.stream(['check-ignore', '-z', '--stdin'], { stdio: [null, null, null] });
child.stdin.end(filePaths.join('\0'), 'utf8');
const onExit = exitCode => {
if (exitCode === 1) {
// nothing ignored
resolve(new Set<string>());
} else if (exitCode === 0) {
// paths are separated by the null-character
resolve(new Set<string>(data.split('\0')));
} else {
reject(new GitError({ stdout: data, stderr, exitCode }));
}
};
let data = '';
const onStdoutData = (raw: string) => {
data += raw;
};
child.stdout.setEncoding('utf8');
child.stdout.on('data', onStdoutData);
let stderr: string = '';
child.stderr.setEncoding('utf8');
child.stderr.on('data', raw => stderr += raw);
child.on('error', reject);
child.on('exit', onExit);
});
});
}
private async run<T>(operation: Operation, runOperation: () => Promise<T> = () => Promise.resolve<any>(null)): Promise<T> {
if (this.state !== RepositoryState.Idle) {
throw new Error('Repository not initialized');
}
const run = async () => {
this._operations = this._operations.start(operation);
this._operations.start(operation);
this._onRunOperation.fire(operation);
try {
@@ -653,7 +780,7 @@ export class Repository implements Disposable {
throw err;
} finally {
this._operations = this._operations.end(operation);
this._operations.end(operation);
this._onDidRunOperation.fire(operation);
}
};
@@ -818,7 +945,7 @@ export class Repository implements Disposable {
await timeout(5000);
}
private async whenIdleAndFocused(): Promise<void> {
async whenIdleAndFocused(): Promise<void> {
while (true) {
if (!this.operations.isIdle()) {
await eventToPromise(this.onDidRunOperation);
@@ -835,6 +962,36 @@ export class Repository implements Disposable {
}
}
get headLabel(): string {
const HEAD = this.HEAD;
if (!HEAD) {
return '';
}
const tag = this.refs.filter(iref => iref.type === RefType.Tag && iref.commit === HEAD.commit)[0];
const tagName = tag && tag.name;
const head = HEAD.name || tagName || (HEAD.commit || '').substr(0, 8);
return head
+ (this.workingTreeGroup.resourceStates.length > 0 ? '*' : '')
+ (this.indexGroup.resourceStates.length > 0 ? '+' : '')
+ (this.mergeGroup.resourceStates.length > 0 ? '!' : '');
}
get syncLabel(): string {
if (!this.HEAD
|| !this.HEAD.name
|| !this.HEAD.commit
|| !this.HEAD.upstream
|| !(this.HEAD.ahead || this.HEAD.behind)
) {
return '';
}
return `${this.HEAD.behind}${this.HEAD.ahead}`;
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
+3 -16
View File
@@ -6,7 +6,7 @@
'use strict';
import { Disposable, Command, EventEmitter, Event } from 'vscode';
import { RefType, Branch } from './git';
import { Branch } from './git';
import { Repository, Operation } from './repository';
import { anyEvent, dispose } from './util';
import * as nls from 'vscode-nls';
@@ -24,20 +24,7 @@ class CheckoutStatusBar {
}
get command(): Command | undefined {
const HEAD = this.repository.HEAD;
if (!HEAD) {
return undefined;
}
const tag = this.repository.refs.filter(iref => iref.type === RefType.Tag && iref.commit === HEAD.commit)[0];
const tagName = tag && tag.name;
const head = HEAD.name || tagName || (HEAD.commit || '').substr(0, 8);
const title = '$(git-branch) '
+ head
+ (this.repository.workingTreeGroup.resourceStates.length > 0 ? '*' : '')
+ (this.repository.indexGroup.resourceStates.length > 0 ? '+' : '')
+ (this.repository.mergeGroup.resourceStates.length > 0 ? '!' : '');
const title = `$(git-branch) ${this.repository.headLabel}`;
return {
command: 'git.checkout',
@@ -112,7 +99,7 @@ class SyncStatusBar {
if (HEAD && HEAD.name && HEAD.commit) {
if (HEAD.upstream) {
if (HEAD.ahead || HEAD.behind) {
text += `${HEAD.behind}${HEAD.ahead}`;
text += this.repository.syncLabel;
}
command = 'git.sync';
tooltip = localize('sync changes', "Synchronize Changes");
+17
View File
@@ -8,6 +8,7 @@
import { Event } from 'vscode';
import { dirname } from 'path';
import * as fs from 'fs';
import * as byline from 'byline';
export function log(...args: any[]): void {
console.log.apply(console, ['git:', ...args]);
@@ -188,4 +189,20 @@ export function find<T>(array: T[], fn: (t: T) => boolean): T | undefined {
});
return result;
}
export async function grep(filename: string, pattern: RegExp): Promise<boolean> {
return new Promise<boolean>((c, e) => {
const fileStream = fs.createReadStream(filename, { encoding: 'utf8' });
const stream = byline(fileStream);
stream.on('data', (line: string) => {
if (pattern.test(line)) {
fileStream.close();
c(true);
}
});
stream.on('error', e);
stream.on('end', () => c(false));
});
}
+6 -1
View File
@@ -50,6 +50,11 @@
"scopeName": "text.git-rebase",
"path": "./syntaxes/git-rebase.tmLanguage.json"
}
]
],
"configurationDefaults": {
"[git-commit]": {
"editor.rulers": [72]
}
}
}
}
+1
View File
@@ -82,6 +82,7 @@
{
"id": "backup-history-server-insight",
"contrib": {
"cacheId": "backup-history-server-insight",
"name": "Backup Status",
"provider": "MSSQL",
"edition": [0,1,2,3,4],
@@ -6,20 +6,20 @@ SUM(ALOC.data_pages) AS data_pages,
(SUM(ALOC.total_pages)*8/1024) AS total_space_MB,
(SUM(ALOC.used_pages)*8/1024) AS used_space_MB,
(SUM(ALOC.data_pages)*8/1024) AS data_space_MB
FROM sys.Tables AS TABL
INNER JOIN sys.Indexes AS INDX
FROM sys.tables AS TABL
INNER JOIN sys.indexes AS INDX
ON TABL.object_id = INDX.object_id
INNER JOIN sys.Partitions AS PART
INNER JOIN sys.partitions AS PART
ON INDX.object_id = PART.object_id
AND INDX.index_id = PART.index_id
INNER JOIN sys.Allocation_Units AS ALOC
INNER JOIN sys.allocation_units AS ALOC
ON PART.partition_id = ALOC.container_id
WHERE
INDX.object_id > 255
AND INDX.index_id <= 1
GROUP BY TABL.name,
GROUP BY TABL.name,
INDX.object_id,
INDX.index_id,
INDX.name
ORDER BY
ORDER BY
(SUM(ALOC.total_pages)*8/1024) DESC
+5 -5
View File
@@ -7,11 +7,11 @@
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceRoot}"
"--extensionDevelopmentPath=${workspaceFolder}"
],
"stopOnEntry": false,
"sourceMaps": true,
"outFiles": ["${workspaceRoot}/client/out"],
"outFiles": ["${workspaceFolder}/client/out"],
"preLaunchTask": "npm"
},
{
@@ -19,10 +19,10 @@
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/client/out/test" ],
"args": ["--extensionDevelopmentPath=${workspaceFolder}", "--extensionTestsPath=${workspaceFolder}/client/out/test" ],
"stopOnEntry": false,
"sourceMaps": true,
"outFiles": ["${workspaceRoot}/client/out/test"],
"outFiles": ["${workspaceFolder}/client/out/test"],
"preLaunchTask": "npm"
},
{
@@ -31,7 +31,7 @@
"request": "attach",
"port": 6004,
"sourceMaps": true,
"outFiles": ["${workspaceRoot}/server/out"]
"outFiles": ["${workspaceFolder}/server/out"]
}
]
+1 -1
View File
@@ -1,5 +1,5 @@
// Available variables which can be used inside of strings.
// ${workspaceRoot}: the root folder of the team
// ${workspaceFolder}: the root folder of the team
// ${file}: the current opened file
// ${fileBasename}: the current opened file's basename
// ${fileDirname}: the current opened file's dirname
+95 -47
View File
@@ -6,21 +6,25 @@
import * as path from 'path';
import { workspace, languages, ExtensionContext, extensions, Uri, TextDocument, ColorRange, Color } from 'vscode';
import { workspace, languages, ExtensionContext, extensions, Uri, TextDocument, ColorInformation, Color, ColorPresentation } from 'vscode';
import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification } from 'vscode-languageclient';
import TelemetryReporter from 'vscode-extension-telemetry';
import { ConfigurationFeature } from 'vscode-languageclient/lib/proposed';
import { DocumentColorRequest } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed';
import { ConfigurationFeature } from 'vscode-languageclient/lib/configuration.proposed';
import { DocumentColorRequest, DocumentColorParams, ColorPresentationParams, ColorPresentationRequest } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed';
import * as nls from 'vscode-nls';
import { hash } from './utils/hash';
let localize = nls.loadMessageBundle();
namespace VSCodeContentRequest {
export const type: RequestType<string, string, any, any> = new RequestType('vscode/content');
}
namespace SchemaContentChangeNotification {
export const type: NotificationType<string, any> = new NotificationType('json/schemaContent');
}
export interface ISchemaAssociations {
[pattern: string]: string[];
}
@@ -56,16 +60,13 @@ interface JSONSchemaSettings {
schema?: any;
}
const ColorFormat_HEX = {
opaque: '"#{red:X}{green:X}{blue:X}"',
transparent: '"#{red:X}{green:X}{blue:X}{alpha:X}"'
};
export function activate(context: ExtensionContext) {
let toDispose = context.subscriptions;
let packageInfo = getPackageInfo(context);
let telemetryReporter: TelemetryReporter = packageInfo && new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey);
context.subscriptions.push(telemetryReporter);
toDispose.push(telemetryReporter);
// The server is implemented in node
let serverModule = context.asAbsolutePath(path.join('server', 'out', 'jsonServerMain.js'));
@@ -102,6 +103,7 @@ export function activate(context: ExtensionContext) {
client.registerFeature(new ConfigurationFeature(client));
let disposable = client.start();
toDispose.push(disposable);
client.onReady().then(() => {
client.onTelemetry(e => {
if (telemetryReporter) {
@@ -119,27 +121,48 @@ export function activate(context: ExtensionContext) {
});
});
let handleContentChange = (uri: Uri) => {
if (uri.scheme === 'vscode' && uri.authority === 'schemas') {
client.sendNotification(SchemaContentChangeNotification.type, uri.toString());
}
};
toDispose.push(workspace.onDidChangeTextDocument(e => handleContentChange(e.document.uri)));
toDispose.push(workspace.onDidCloseTextDocument(d => handleContentChange(d.uri)));
client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociation(context));
// register color provider
context.subscriptions.push(languages.registerColorProvider(documentSelector, {
provideDocumentColors(document: TextDocument): Thenable<ColorRange[]> {
let params = client.code2ProtocolConverter.asDocumentSymbolParams(document);
toDispose.push(languages.registerColorProvider(documentSelector, {
provideDocumentColors(document: TextDocument): Thenable<ColorInformation[]> {
let params: DocumentColorParams = {
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document)
};
return client.sendRequest(DocumentColorRequest.type, params).then(symbols => {
return symbols.map(symbol => {
let range = client.protocol2CodeConverter.asRange(symbol.range);
let color = new Color(symbol.color.red * 255, symbol.color.green * 255, symbol.color.blue * 255, symbol.color.alpha);
return new ColorRange(range, color, [ColorFormat_HEX]);
let color = new Color(symbol.color.red, symbol.color.green, symbol.color.blue, symbol.color.alpha);
return new ColorInformation(range, color);
});
});
},
provideColorPresentations(color: Color, context): Thenable<ColorPresentation[]> {
let params: ColorPresentationParams = {
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(context.document),
color: color,
range: client.code2ProtocolConverter.asRange(context.range)
};
return client.sendRequest(ColorPresentationRequest.type, params).then(presentations => {
return presentations.map(p => {
let presentation = new ColorPresentation(p.label);
presentation.textEdit = p.textEdit && client.protocol2CodeConverter.asTextEdit(p.textEdit);
presentation.additionalTextEdits = p.additionalTextEdits && client.protocol2CodeConverter.asTextEdits(p.additionalTextEdits);
return presentation;
});
});
}
}));
});
// Push the disposable to the context's subscriptions so that the
// client can be deactivated on extension deactivation
context.subscriptions.push(disposable);
languages.setLanguageConfiguration('json', {
wordPattern: /("(?:[^\\\"]*(?:\\.)?)*"?)|[^\s{}\[\],:]+/,
indentationRules: {
@@ -184,9 +207,6 @@ function getSchemaAssociation(context: ExtensionContext): ISchemaAssociations {
function getSettings(): Settings {
let httpSettings = workspace.getConfiguration('http');
let jsonSettings = workspace.getConfiguration('json');
let schemas = [];
let settings: Settings = {
http: {
@@ -194,42 +214,70 @@ function getSettings(): Settings {
proxyStrictSSL: httpSettings.get('proxyStrictSSL')
},
json: {
format: jsonSettings.get('format'),
schemas: schemas,
format: workspace.getConfiguration('json').get('format'),
schemas: [],
}
};
let schemaSettingsById: { [schemaId: string]: JSONSchemaSettings } = Object.create(null);
let collectSchemaSettings = (schemaSettings: JSONSchemaSettings[], rootPath?: string, fileMatchPrefix?: string) => {
for (let setting of schemaSettings) {
let url = getSchemaId(setting, rootPath);
if (!url) {
continue;
}
let schemaSetting = schemaSettingsById[url];
if (!schemaSetting) {
schemaSetting = schemaSettingsById[url] = { url, fileMatch: [] };
settings.json.schemas.push(schemaSetting);
}
let fileMatches = setting.fileMatch;
if (Array.isArray(fileMatches)) {
if (fileMatchPrefix) {
fileMatches = fileMatches.map(m => fileMatchPrefix + m);
}
schemaSetting.fileMatch.push(...fileMatches);
}
if (setting.schema) {
schemaSetting.schema = setting.schema;
}
}
};
let settingsSchemas = jsonSettings.get('schemas');
if (Array.isArray(settingsSchemas)) {
schemas.push(...settingsSchemas);
}
// merge global and folder settings. Qualify all file matches with the folder path.
let globalSettings = workspace.getConfiguration('json', null).get<JSONSchemaSettings[]>('schemas');
if (Array.isArray(globalSettings)) {
collectSchemaSettings(globalSettings, workspace.rootPath);
}
let folders = workspace.workspaceFolders;
if (folders) {
folders.forEach(folder => {
let jsonConfig = workspace.getConfiguration('json', folder.uri);
let schemaConfigInfo = jsonConfig.inspect<JSONSchemaSettings[]>('schemas');
for (let folder of folders) {
let folderUri = folder.uri;
let schemaConfigInfo = workspace.getConfiguration('json', folderUri).inspect<JSONSchemaSettings[]>('schemas');
let folderSchemas = schemaConfigInfo.workspaceFolderValue;
if (Array.isArray(folderSchemas)) {
folderSchemas.forEach(schema => {
let url = schema.url;
if (!url && schema.schema) {
url = schema.schema.id;
}
if (url && url[0] === '.') {
url = Uri.file(path.normalize(path.join(folder.uri.fsPath, url))).toString();
}
let fileMatch = schema.fileMatch;
if (fileMatch) {
fileMatch = fileMatch.map(m => folder.uri.toString() + '*' + m);
}
schemas.push({ url, fileMatch, schema: schema.schema });
});
let folderPath = folderUri.toString();
if (folderPath[folderPath.length - 1] !== '/') {
folderPath = folderPath + '/';
}
collectSchemaSettings(folderSchemas, folderUri.fsPath, folderPath + '*');
};
});
};
}
return settings;
}
function getSchemaId(schema: JSONSchemaSettings, rootPath?: string) {
let url = schema.url;
if (!url) {
if (schema.schema) {
url = schema.schema.id || `vscode://schemas/custom/${encodeURIComponent(hash(schema.schema).toString(16))}`;
}
} else if (rootPath && (url[0] === '.' || url[0] === '/')) {
url = Uri.file(path.normalize(path.join(rootPath, url))).toString();
}
return url;
}
function getPackageInfo(context: ExtensionContext): IPackageInfo {
let extensionPackage = require(context.asAbsolutePath('./package.json'));
if (extensionPackage) {
@@ -240,4 +288,4 @@ function getPackageInfo(context: ExtensionContext): IPackageInfo {
};
}
return null;
}
}
+59
View File
@@ -0,0 +1,59 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
/**
* Return a hash value for an object.
*/
export function hash(obj: any, hashVal = 0): number {
switch (typeof obj) {
case 'object':
if (obj === null) {
return numberHash(349, hashVal);
} else if (Array.isArray(obj)) {
return arrayHash(obj, hashVal);
}
return objectHash(obj, hashVal);
case 'string':
return stringHash(obj, hashVal);
case 'boolean':
return booleanHash(obj, hashVal);
case 'number':
return numberHash(obj, hashVal);
case 'undefined':
return numberHash(obj, 937);
default:
return numberHash(obj, 617);
}
}
function numberHash(val: number, initialHashVal: number): number {
return (((initialHashVal << 5) - initialHashVal) + val) | 0; // hashVal * 31 + ch, keep as int32
}
function booleanHash(b: boolean, initialHashVal: number): number {
return numberHash(b ? 433 : 863, initialHashVal);
}
function stringHash(s: string, hashVal: number) {
hashVal = numberHash(149417, hashVal);
for (let i = 0, length = s.length; i < length; i++) {
hashVal = numberHash(s.charCodeAt(i), hashVal);
}
return hashVal;
}
function arrayHash(arr: any[], initialHashVal: number): number {
initialHashVal = numberHash(104579, initialHashVal);
return arr.reduce((hashVal, item) => hash(item, hashVal), initialHashVal);
}
function objectHash(obj: any, initialHashVal: number): number {
initialHashVal = numberHash(181387, initialHashVal);
return Object.keys(obj).sort().reduce((hashVal, key) => {
hashVal = stringHash(key, hashVal);
return hash(obj[key], hashVal);
}, initialHashVal);
}
+12 -12
View File
@@ -13,24 +13,24 @@
"resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.8.tgz"
},
"vscode-jsonrpc": {
"version": "3.3.1",
"from": "vscode-jsonrpc@>=3.3.0 <4.0.0",
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.3.1.tgz"
"version": "3.5.0-next.2",
"from": "vscode-jsonrpc@3.5.0-next.2",
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.5.0-next.2.tgz"
},
"vscode-languageclient": {
"version": "3.4.0-next.17",
"from": "vscode-languageclient@next",
"resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-3.4.0-next.17.tgz"
"version": "3.5.0-next.4",
"from": "vscode-languageclient@3.5.0-next.4",
"resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-3.5.0-next.4.tgz"
},
"vscode-languageserver-protocol": {
"version": "3.1.1",
"from": "vscode-languageserver-protocol@>=3.1.1 <4.0.0",
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.1.1.tgz"
"version": "3.5.0-next.5",
"from": "vscode-languageserver-protocol@3.5.0-next.5",
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.5.0-next.5.tgz"
},
"vscode-languageserver-types": {
"version": "3.3.0",
"from": "vscode-languageserver-types@>=3.3.0 <4.0.0",
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.3.0.tgz"
"version": "3.5.0-next.2",
"from": "vscode-languageserver-types@3.5.0-next.2",
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.5.0-next.2.tgz"
},
"vscode-nls": {
"version": "2.0.2",
+2 -3
View File
@@ -133,11 +133,10 @@
},
"dependencies": {
"vscode-extension-telemetry": "0.0.8",
"vscode-languageclient": "3.4.0-next.17",
"vscode-languageserver-protocol": "^3.1.1",
"vscode-languageclient": "3.5.0-next.4",
"vscode-nls": "2.0.2"
},
"devDependencies": {
"@types/node": "^6.0.51"
"@types/node": "7.0.43"
}
}
+4 -4
View File
@@ -8,25 +8,25 @@
"request": "attach",
"port": 6004,
"sourceMaps": true,
"outDir": "${workspaceRoot}/out"
"outDir": "${workspaceFolder}/out"
},
{
"name": "Unit Tests",
"type": "node",
"request": "launch",
"program": "${workspaceRoot}/../../../node_modules/mocha/bin/_mocha",
"program": "${workspaceFolder}/../../../node_modules/mocha/bin/_mocha",
"stopOnEntry": false,
"args": [
"--timeout",
"999999",
"--colors"
],
"cwd": "${workspaceRoot}",
"cwd": "${workspaceFolder}",
"runtimeExecutable": null,
"runtimeArgs": [],
"env": {},
"sourceMaps": true,
"outDir": "${workspaceRoot}/out"
"outDir": "${workspaceFolder}/out"
}
]
}
+14 -14
View File
@@ -43,29 +43,29 @@
"resolved": "https://registry.npmjs.org/request-light/-/request-light-0.2.1.tgz"
},
"vscode-json-languageservice": {
"version": "2.0.16",
"version": "3.0.0",
"from": "vscode-json-languageservice@next",
"resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-2.0.16.tgz"
"resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-3.0.0.tgz"
},
"vscode-jsonrpc": {
"version": "3.3.1",
"from": "vscode-jsonrpc@>=3.3.0 <4.0.0",
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.3.1.tgz"
"version": "3.5.0-next.2",
"from": "vscode-jsonrpc@3.5.0-next.2",
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.5.0-next.2.tgz"
},
"vscode-languageserver": {
"version": "3.4.0-next.6",
"from": "vscode-languageserver@next",
"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.4.0-next.6.tgz"
"version": "3.5.0-next.6",
"from": "vscode-languageserver@3.5.0-next.6",
"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.5.0-next.6.tgz"
},
"vscode-languageserver-protocol": {
"version": "3.1.1",
"from": "vscode-languageserver-protocol@>=3.1.1 <4.0.0",
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.1.1.tgz"
"version": "3.5.0-next.5",
"from": "vscode-languageserver-protocol@3.5.0-next.5",
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.5.0-next.5.tgz"
},
"vscode-languageserver-types": {
"version": "3.3.0",
"from": "vscode-languageserver-types@>=3.0.3 <4.0.0",
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.3.0.tgz"
"version": "3.5.0-next.2",
"from": "vscode-languageserver-types@3.5.0-next.2",
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.5.0-next.2.tgz"
},
"vscode-nls": {
"version": "2.0.2",
+5 -5
View File
@@ -10,13 +10,13 @@
"dependencies": {
"jsonc-parser": "^1.0.0",
"request-light": "^0.2.1",
"vscode-json-languageservice": "^2.0.16",
"vscode-languageserver": "3.4.0-next.6",
"vscode-languageserver-protocol": "^3.1.1",
"vscode-nls": "^2.0.2"
"vscode-json-languageservice": "3.0.0",
"vscode-languageserver": "3.5.0-next.6",
"vscode-nls": "^2.0.2",
"vscode-uri": "^1.0.1"
},
"devDependencies": {
"@types/node": "^6.0.51"
"@types/node": "7.0.43"
},
"scripts": {
"compile": "gulp compile-extension:json-server",
+22 -14
View File
@@ -10,12 +10,11 @@ import {
DocumentRangeFormattingRequest, Disposable, ServerCapabilities
} from 'vscode-languageserver';
import { DocumentColorRequest, ServerCapabilities as CPServerCapabilities } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed';
import { DocumentColorRequest, ServerCapabilities as CPServerCapabilities, ColorPresentationRequest } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed';
import { xhr, XHRResponse, configure as configureHttpRequests, getErrorStatusDescription } from 'request-light';
import path = require('path');
import fs = require('fs');
import URI from './utils/uri';
import URI from 'vscode-uri';
import * as URL from 'url';
import Strings = require('./utils/strings');
import { JSONDocument, JSONSchema, LanguageSettings, getLanguageService } from 'vscode-json-languageservice';
@@ -36,6 +35,10 @@ namespace VSCodeContentRequest {
export const type: RequestType<string, string, any, any> = new RequestType('vscode/content');
}
namespace SchemaContentChangeNotification {
export const type: NotificationType<string, any> = new NotificationType('json/schemaContent');
}
// Create a connection for the server
let connection: IConnection = createConnection();
@@ -54,9 +57,7 @@ let clientDynamicRegisterSupport = false;
// After the server has started the client sends an initilize request. The server receives
// in the passed params the rootPath of the workspace plus the client capabilities.
let workspaceRoot: URI;
connection.onInitialize((params: InitializeParams): InitializeResult => {
workspaceRoot = URI.parse(params.rootPath);
function hasClientCapability(...keys: string[]) {
let c = params.capabilities;
@@ -175,6 +176,11 @@ connection.onNotification(SchemaAssociationNotification.type, associations => {
updateConfiguration();
});
// A schema has changed
connection.onNotification(SchemaContentChangeNotification.type, uri => {
languageService.resetSchema(uri);
});
function updateConfiguration() {
let languageSettings: LanguageSettings = {
validate: true,
@@ -192,19 +198,12 @@ function updateConfiguration() {
}
}
if (jsonConfigurationSettings) {
jsonConfigurationSettings.forEach(schema => {
jsonConfigurationSettings.forEach((schema, index) => {
let uri = schema.url;
if (!uri && schema.schema) {
uri = schema.schema.id;
}
if (!uri && schema.fileMatch) {
uri = 'vscode://schemas/custom/' + encodeURIComponent(schema.fileMatch.join('&'));
uri = schema.schema.id || `vscode://schemas/custom/${index}`;
}
if (uri) {
if (uri[0] === '.' && workspaceRoot) {
// workspace relative path
uri = URI.file(path.normalize(path.join(workspaceRoot.fsPath, uri))).toString();
}
languageSettings.schemas.push({ uri, fileMatch: schema.fileMatch, schema: schema.schema });
}
});
@@ -321,5 +320,14 @@ connection.onRequest(DocumentColorRequest.type, params => {
return [];
});
connection.onRequest(ColorPresentationRequest.type, params => {
let document = documents.get(params.textDocument.uri);
if (document) {
let jsonDocument = getJSONDocument(document);
return languageService.getColorPresentations(document, jsonDocument, params.color, params.range);
}
return [];
});
// Listen on the connection
connection.listen();
@@ -388,11 +388,11 @@
"c": "\\u0056",
"t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json string.quoted.double.json constant.character.escape.json",
"r": {
"dark_plus": "string: #CE9178",
"light_plus": "string: #A31515",
"dark_plus": "constant.character.escape: #D7BA7D",
"light_plus": "constant.character.escape: #A31515",
"dark_vs": "string: #CE9178",
"light_vs": "string: #A31515",
"hc_black": "string: #CE9178"
"hc_black": "constant.character: #569CD6"
}
},
{
@@ -37,5 +37,8 @@
["(", ")"],
["[", "]"],
["`", "`"]
]
],
"folding": {
"offSide": true
}
}
+132 -18
View File
@@ -2,75 +2,189 @@
"name": "vscode-markdown",
"version": "0.2.0",
"dependencies": {
"@types/highlight.js": {
"version": "9.1.10",
"from": "@types/highlight.js@9.1.10",
"resolved": "https://registry.npmjs.org/@types/highlight.js/-/highlight.js-9.1.10.tgz",
"dev": true
},
"@types/markdown-it": {
"version": "0.0.2",
"from": "@types/markdown-it@0.0.2",
"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-0.0.2.tgz",
"dev": true
},
"@types/node": {
"version": "7.0.43",
"from": "@types/node@7.0.43",
"resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.43.tgz",
"dev": true
},
"applicationinsights": {
"version": "0.18.0",
"from": "applicationinsights@0.18.0",
"from": "applicationinsights@https://registry.npmjs.org/applicationinsights/-/applicationinsights-0.18.0.tgz",
"resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-0.18.0.tgz"
},
"argparse": {
"version": "1.0.9",
"from": "argparse@>=1.0.7 <2.0.0",
"from": "argparse@https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz"
},
"binaryextensions": {
"version": "1.0.1",
"from": "binaryextensions@1.0.1",
"resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-1.0.1.tgz",
"dev": true
},
"core-util-is": {
"version": "1.0.2",
"from": "core-util-is@1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"dev": true
},
"entities": {
"version": "1.1.1",
"from": "entities@>=1.1.1 <1.2.0",
"from": "entities@https://registry.npmjs.org/entities/-/entities-1.1.1.tgz",
"resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz"
},
"escape-string-regexp": {
"version": "1.0.5",
"from": "escape-string-regexp@1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"dev": true
},
"gulp-rename": {
"version": "1.2.2",
"from": "gulp-rename@1.2.2",
"resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.2.2.tgz",
"dev": true
},
"gulp-replace": {
"version": "0.5.4",
"from": "gulp-replace@0.5.4",
"resolved": "https://registry.npmjs.org/gulp-replace/-/gulp-replace-0.5.4.tgz",
"dev": true
},
"highlight.js": {
"version": "9.5.0",
"from": "highlight.js@>=9.3.0 <10.0.0",
"from": "highlight.js@https://registry.npmjs.org/highlight.js/-/highlight.js-9.5.0.tgz",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.5.0.tgz"
},
"inherits": {
"version": "2.0.3",
"from": "inherits@2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"dev": true
},
"isarray": {
"version": "1.0.0",
"from": "isarray@1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"dev": true
},
"istextorbinary": {
"version": "1.0.2",
"from": "istextorbinary@1.0.2",
"resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-1.0.2.tgz",
"dev": true
},
"linkify-it": {
"version": "2.0.3",
"from": "linkify-it@>=2.0.0 <3.0.0",
"from": "linkify-it@https://registry.npmjs.org/linkify-it/-/linkify-it-2.0.3.tgz",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.0.3.tgz"
},
"markdown-it": {
"version": "8.2.2",
"from": "markdown-it@8.2.2",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.2.2.tgz"
"version": "8.4.0",
"from": "markdown-it@8.4.0",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.0.tgz"
},
"markdown-it-named-headers": {
"version": "0.0.4",
"from": "markdown-it-named-headers@0.0.4",
"from": "markdown-it-named-headers@https://registry.npmjs.org/markdown-it-named-headers/-/markdown-it-named-headers-0.0.4.tgz",
"resolved": "https://registry.npmjs.org/markdown-it-named-headers/-/markdown-it-named-headers-0.0.4.tgz"
},
"mdurl": {
"version": "1.0.1",
"from": "mdurl@>=1.0.1 <1.1.0",
"from": "mdurl@https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz"
},
"object-assign": {
"version": "4.1.1",
"from": "object-assign@4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"dev": true
},
"process-nextick-args": {
"version": "1.0.7",
"from": "process-nextick-args@1.0.7",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
"dev": true
},
"readable-stream": {
"version": "2.3.3",
"from": "readable-stream@2.3.3",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
"dev": true
},
"replacestream": {
"version": "4.0.2",
"from": "replacestream@4.0.2",
"resolved": "https://registry.npmjs.org/replacestream/-/replacestream-4.0.2.tgz",
"dev": true
},
"safe-buffer": {
"version": "5.1.1",
"from": "safe-buffer@5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"dev": true
},
"sprintf-js": {
"version": "1.0.3",
"from": "sprintf-js@>=1.0.2 <1.1.0",
"from": "sprintf-js@https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz"
},
"string": {
"version": "3.3.1",
"from": "string@>=3.0.1 <4.0.0",
"from": "string@https://registry.npmjs.org/string/-/string-3.3.1.tgz",
"resolved": "https://registry.npmjs.org/string/-/string-3.3.1.tgz"
},
"string_decoder": {
"version": "1.0.3",
"from": "string_decoder@1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"dev": true
},
"textextensions": {
"version": "1.0.2",
"from": "textextensions@1.0.2",
"resolved": "https://registry.npmjs.org/textextensions/-/textextensions-1.0.2.tgz",
"dev": true
},
"uc.micro": {
"version": "1.0.3",
"from": "uc.micro@>=1.0.3 <2.0.0",
"from": "uc.micro@https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.3.tgz",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.3.tgz"
},
"util-deprecate": {
"version": "1.0.2",
"from": "util-deprecate@1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"dev": true
},
"vscode-extension-telemetry": {
"version": "0.0.7",
"from": "vscode-extension-telemetry@>=0.0.8 <0.0.9",
"version": "0.0.8",
"from": "vscode-extension-telemetry@https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.8.tgz",
"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",
"from": "vscode-nls@https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz",
"resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz"
},
"winreg": {
"version": "1.2.3",
"from": "winreg@1.2.3",
"from": "winreg@https://registry.npmjs.org/winreg/-/winreg-1.2.3.tgz",
"resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.3.tgz"
}
}
}
}
+84 -17
View File
@@ -41,7 +41,52 @@
{
"language": "markdown",
"scopeName": "text.html.markdown",
"path": "./syntaxes/markdown.tmLanguage"
"path": "./syntaxes/markdown.tmLanguage",
"embeddedLanguages": {
"meta.embedded.block.html": "html",
"source.js": "javascript",
"source.css": "css",
"meta.embedded.block.frontmatter": "yaml",
"meta.embedded.block.css": "css",
"meta.embedded.block.ini": "ini",
"meta.embedded.block.java": "java",
"meta.embedded.block.lua": "lua",
"meta.embedded.block.makefile": "makefile",
"meta.embedded.block.perl": "perl",
"meta.embedded.block.r": "r",
"meta.embedded.block.ruby": "ruby",
"meta.embedded.block.php": "php",
"meta.embedded.block.sql": "sql",
"meta.embedded.block.vs_net": "vs_net",
"meta.embedded.block.xml": "xml",
"meta.embedded.block.xsl": "xsl",
"meta.embedded.block.yaml": "yaml",
"meta.embedded.block.dosbatch": "dosbatch",
"meta.embedded.block.clojure": "clojure",
"meta.embedded.block.coffee": "coffee",
"meta.embedded.block.c": "c",
"meta.embedded.block.cpp": "cpp",
"meta.embedded.block.diff": "diff",
"meta.embedded.block.dockerfile": "dockerfile",
"meta.embedded.block.go": "go",
"meta.embedded.block.groovy": "groovy",
"meta.embedded.block.jade": "jade",
"meta.embedded.block.javascript": "javascript",
"meta.embedded.block.json": "json",
"meta.embedded.block.less": "less",
"meta.embedded.block.objc": "objc",
"meta.embedded.block.scss": "scss",
"meta.embedded.block.perl6": "perl6",
"meta.embedded.block.powershell": "powershell",
"meta.embedded.block.python": "python",
"meta.embedded.block.rust": "rust",
"meta.embedded.block.scala": "scala",
"meta.embedded.block.shellscript": "shellscript",
"meta.embedded.block.typescript": "typescript",
"meta.embedded.block.typescriptreact": "typescriptreact",
"meta.embedded.block.csharp": "csharp",
"meta.embedded.block.fsharp": "fsharp"
}
}
],
"commands": [
@@ -131,6 +176,10 @@
{
"command": "markdown.showPreviewSecuritySelector",
"when": "editorLangId == markdown"
},
{
"command": "markdown.showPreviewSecuritySelector",
"when": "resourceScheme == markdown"
}
]
},
@@ -162,7 +211,8 @@
"markdown.styles": {
"type": "array",
"default": [],
"description": "%markdown.styles.dec%"
"description": "%markdown.styles.dec%",
"scope": "resource"
},
"markdown.previewFrontMatter": {
"type": "string",
@@ -171,52 +221,62 @@
"show"
],
"default": "hide",
"description": "%markdown.previewFrontMatter.dec%"
"description": "%markdown.previewFrontMatter.dec%",
"scope": "resource"
},
"markdown.preview.breaks": {
"type": "boolean",
"default": false,
"description": "%markdown.preview.breaks.desc%"
"description": "%markdown.preview.breaks.desc%",
"scope": "resource"
},
"markdown.preview.linkify": {
"type": "boolean",
"default": true,
"description": "%markdown.preview.linkify%"
"description": "%markdown.preview.linkify%",
"scope": "resource"
},
"markdown.preview.fontFamily": {
"type": "string",
"default": "-apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', 'HelveticaNeue-Light', 'Ubuntu', 'Droid Sans', sans-serif",
"description": "%markdown.preview.fontFamily.desc%"
"description": "%markdown.preview.fontFamily.desc%",
"scope": "resource"
},
"markdown.preview.fontSize": {
"type": "number",
"default": 14,
"description": "%markdown.preview.fontSize.desc%"
"description": "%markdown.preview.fontSize.desc%",
"scope": "resource"
},
"markdown.preview.lineHeight": {
"type": "number",
"default": 1.6,
"description": "%markdown.preview.lineHeight.desc%"
"description": "%markdown.preview.lineHeight.desc%",
"scope": "resource"
},
"markdown.preview.scrollPreviewWithEditorSelection": {
"type": "boolean",
"default": true,
"description": "%markdown.preview.scrollPreviewWithEditorSelection.desc%"
"description": "%markdown.preview.scrollPreviewWithEditorSelection.desc%",
"scope": "resource"
},
"markdown.preview.markEditorSelection": {
"type": "boolean",
"default": true,
"description": "%markdown.preview.markEditorSelection.desc%"
"description": "%markdown.preview.markEditorSelection.desc%",
"scope": "resource"
},
"markdown.preview.scrollEditorWithPreview": {
"type": "boolean",
"default": true,
"description": "%markdown.preview.scrollEditorWithPreview.desc%"
"description": "%markdown.preview.scrollEditorWithPreview.desc%",
"scope": "resource"
},
"markdown.preview.doubleClickToSwitchToEditor": {
"type": "boolean",
"default": true,
"description": "%markdown.preview.doubleClickToSwitchToEditor.desc%"
"description": "%markdown.preview.doubleClickToSwitchToEditor.desc%",
"scope": "resource"
},
"markdown.trace": {
"type": "string",
@@ -225,7 +285,8 @@
"verbose"
],
"default": "off",
"description": "%markdown.trace.desc%"
"description": "%markdown.trace.desc%",
"scope": "window"
}
}
},
@@ -234,7 +295,13 @@
"editor.wordWrap": "on",
"editor.quickSuggestions": false
}
}
},
"jsonValidation": [
{
"fileMatch": "package.json",
"url": "./schemas/package.schema.json"
}
]
},
"scripts": {
"vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:markdown ./tsconfig.json",
@@ -242,15 +309,15 @@
},
"dependencies": {
"highlight.js": "9.5.0",
"markdown-it": "8.2.2",
"markdown-it": "^8.4.0",
"markdown-it-named-headers": "0.0.4",
"vscode-extension-telemetry": "0.0.8",
"vscode-nls": "2.0.2"
},
"devDependencies": {
"@types/highlight.js": "^9.1.9",
"@types/highlight.js": "9.1.10",
"@types/markdown-it": "0.0.2",
"@types/node": "^7.0.4",
"@types/node": "8.0.33",
"gulp-rename": "^1.2.2",
"gulp-replace": "^0.5.4"
}
@@ -0,0 +1,32 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Markdown contributions to package.json",
"type": "object",
"properties": {
"contributes": {
"type": "object",
"properties": {
"markdown.previewStyles": {
"type": "array",
"description": "Contributed CSS files that change the look or layout of the Markdown preview",
"items": {
"type": "string",
"description": "Extension relative path to a css file"
}
},
"markdown.previewScripts": {
"type": "array",
"description": "Contributed scripts that are executed in the Markdown preview",
"items": {
"type": "string",
"description": "Extension relative path to a JavaScript file"
}
},
"markdown.markdownItPlugins": {
"type": "boolean",
"description": "Does this extension contribute a markdown-it plugin?"
}
}
}
}
}
+114 -25
View File
@@ -8,20 +8,61 @@
import * as vscode from 'vscode';
import * as path from 'path';
export default class MarkdownDocumentLinkProvider implements vscode.DocumentLinkProvider {
function normalizeLink(document: vscode.TextDocument, link: string, base: string): vscode.Uri {
const uri = vscode.Uri.parse(link);
if (uri.scheme) {
return uri;
}
private _linkPattern = /(\[[^\]]*\]\(\s*?)(((((?=.*\)\)+)|(?=.*\)\]+))[^\s\)]+?)|([^\s]+)))\)/g;
// assume it must be a file
let resourcePath = uri.path;
if (!uri.path) {
resourcePath = document.uri.path;
} else if (uri.path[0] === '/') {
const root = vscode.workspace.getWorkspaceFolder(document.uri);
if (root) {
resourcePath = path.join(root.uri.fsPath, uri.path);
}
} else {
resourcePath = path.join(base, uri.path);
}
constructor() { }
return vscode.Uri.parse(`command:_markdown.openDocumentLink?${encodeURIComponent(JSON.stringify({ fragment: uri.fragment, path: resourcePath }))}`);
}
public provideDocumentLinks(document: vscode.TextDocument, _token: vscode.CancellationToken): vscode.DocumentLink[] {
const results: vscode.DocumentLink[] = [];
function matchAll(pattern: RegExp, text: string): Array<RegExpMatchArray> {
const out: RegExpMatchArray[] = [];
pattern.lastIndex = 0;
let match: RegExpMatchArray | null;
while ((match = pattern.exec(text))) {
out.push(match);
}
return out;
}
export default class LinkProvider implements vscode.DocumentLinkProvider {
private linkPattern = /(\[[^\]]*\]\(\s*?)(((((?=.*\)\)+)|(?=.*\)\]+))[^\s\)]+?)|([^\s]+)))\)/g;
private referenceLinkPattern = /(\[([^\]]+)\]\[\s*?)([^\s\]]*?)\]/g;
private definitionPattern = /^([\t ]*\[([^\]]+)\]:\s*)(\S+)/gm;
public provideDocumentLinks(
document: vscode.TextDocument,
_token: vscode.CancellationToken
): vscode.DocumentLink[] {
const base = path.dirname(document.uri.fsPath);
const text = document.getText();
this._linkPattern.lastIndex = 0;
let match: RegExpMatchArray | null;
while ((match = this._linkPattern.exec(text))) {
return this.providerInlineLinks(text, document, base)
.concat(this.provideReferenceLinks(text, document, base));
}
private providerInlineLinks(
text: string,
document: vscode.TextDocument,
base: string
): vscode.DocumentLink[] {
const results: vscode.DocumentLink[] = [];
for (const match of matchAll(this.linkPattern, text)) {
const pre = match[1];
const link = match[2];
const offset = (match.index || 0) + pre.length;
@@ -30,7 +71,7 @@ export default class MarkdownDocumentLinkProvider implements vscode.DocumentLink
try {
results.push(new vscode.DocumentLink(
new vscode.Range(linkStart, linkEnd),
this.normalizeLink(document, link, base)));
normalizeLink(document, link, base)));
} catch (e) {
// noop
}
@@ -39,25 +80,73 @@ export default class MarkdownDocumentLinkProvider implements vscode.DocumentLink
return results;
}
private normalizeLink(document: vscode.TextDocument, link: string, base: string): vscode.Uri {
const uri = vscode.Uri.parse(link);
if (uri.scheme) {
return uri;
}
private provideReferenceLinks(
text: string,
document: vscode.TextDocument,
base: string
): vscode.DocumentLink[] {
const results: vscode.DocumentLink[] = [];
// assume it must be a file
let resourcePath = uri.path;
if (!uri.path) {
resourcePath = document.uri.path;
} else if (uri.path[0] === '/') {
const root = vscode.workspace.getWorkspaceFolder(document.uri);
if (root) {
resourcePath = path.join(root.uri.fsPath, uri.path);
const definitions = this.getDefinitions(text, document);
for (const match of matchAll(this.referenceLinkPattern, text)) {
let linkStart: vscode.Position;
let linkEnd: vscode.Position;
let reference = match[3];
if (reference) { // [text][ref]
const pre = match[1];
const offset = (match.index || 0) + pre.length;
linkStart = document.positionAt(offset);
linkEnd = document.positionAt(offset + reference.length);
} else if (match[2]) { // [ref][]
reference = match[2];
const offset = (match.index || 0) + 1;
linkStart = document.positionAt(offset);
linkEnd = document.positionAt(offset + match[2].length);
} else {
continue;
}
try {
const link = definitions.get(reference);
if (link) {
results.push(new vscode.DocumentLink(
new vscode.Range(linkStart, linkEnd),
vscode.Uri.parse(`command:_markdown.moveCursorToPosition?${encodeURIComponent(JSON.stringify([link.linkRange.start.line, link.linkRange.start.character]))}`)));
}
} catch (e) {
// noop
}
} else {
resourcePath = path.join(base, uri.path);
}
return vscode.Uri.parse(`command:_markdown.openDocumentLink?${encodeURIComponent(JSON.stringify({ fragment: uri.fragment, path: resourcePath }))}`);
for (const definition of Array.from(definitions.values())) {
try {
results.push(new vscode.DocumentLink(
definition.linkRange,
normalizeLink(document, definition.link, base)));
} catch (e) {
// noop
}
}
return results;
}
private getDefinitions(text: string, document: vscode.TextDocument) {
const out = new Map<string, { link: string, linkRange: vscode.Range }>();
for (const match of matchAll(this.definitionPattern, text)) {
const pre = match[1];
const reference = match[2];
const link = match[3].trim();
const offset = (match.index || 0) + pre.length;
const linkStart = document.positionAt(offset);
const linkEnd = document.positionAt(offset + link.length);
out.set(reference, {
link: link,
linkRange: new vscode.Range(linkStart, linkEnd)
});
}
return out;
}
}
+21 -5
View File
@@ -11,7 +11,7 @@ import * as vscode from 'vscode';
import * as path from 'path';
import TelemetryReporter from 'vscode-extension-telemetry';
import { MarkdownEngine } from './markdownEngine';
import DocumentLinkProvider from './documentLinkProvider';
import LinkProvider from './documentLinkProvider';
import MDDocumentSymbolProvider from './documentSymbolProvider';
import { ExtensionContentSecurityPolicyArbiter, PreviewSecuritySelector } from './security';
import { MDDocumentContentProvider, getMarkdownUri, isMarkdownFile } from './previewContentProvider';
@@ -96,18 +96,28 @@ export function activate(context: vscode.ExtensionContext) {
const symbolsProviderRegistration = vscode.languages.registerDocumentSymbolProvider({ language: 'markdown' }, symbolsProvider);
context.subscriptions.push(contentProviderRegistration, symbolsProviderRegistration);
context.subscriptions.push(vscode.languages.registerDocumentLinkProvider('markdown', new DocumentLinkProvider()));
context.subscriptions.push(vscode.languages.registerDocumentLinkProvider('markdown', new LinkProvider()));
context.subscriptions.push(vscode.commands.registerCommand('markdown.showPreview', (uri) => showPreview(cspArbiter, uri, false)));
context.subscriptions.push(vscode.commands.registerCommand('markdown.showPreviewToSide', uri => showPreview(cspArbiter, uri, true)));
context.subscriptions.push(vscode.commands.registerCommand('markdown.showSource', showSource));
context.subscriptions.push(vscode.commands.registerCommand('_markdown.moveCursorToPosition', (line: number, character: number) => {
if (!vscode.window.activeTextEditor) {
return;
}
const position = new vscode.Position(line, character);
const selection = new vscode.Selection(position, position);
vscode.window.activeTextEditor.revealRange(selection);
vscode.window.activeTextEditor.selection = selection;
}));
context.subscriptions.push(vscode.commands.registerCommand('_markdown.revealLine', (uri, line) => {
const sourceUri = vscode.Uri.parse(decodeURIComponent(uri));
logger.log('revealLine', { uri, sourceUri: sourceUri.toString(), line });
vscode.window.visibleTextEditors
.filter(editor => isMarkdownFile(editor.document) && editor.document.uri.fsPath === sourceUri.fsPath)
.filter(editor => isMarkdownFile(editor.document) && editor.document.uri.toString() === sourceUri.toString())
.forEach(editor => {
const sourceLine = Math.floor(line);
const fraction = line - sourceLine;
@@ -243,13 +253,19 @@ function showPreview(cspArbiter: ExtensionContentSecurityPolicyArbiter, uri?: vs
const thenable = vscode.commands.executeCommand('vscode.previewHtml',
getMarkdownUri(resource),
getViewColumn(sideBySide),
`Preview '${path.basename(resource.fsPath)}'`,
localize('previewTitle', 'Preview {0}', path.basename(resource.fsPath)),
{
allowScripts: true,
allowSvgs: cspArbiter.shouldAllowSvgsForResource(resource)
});
if (telemetryReporter) {
/* __GDPR__
"openPreview" : {
"where" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"how": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
telemetryReporter.sendTelemetryEvent('openPreview', {
where: sideBySide ? 'sideBySide' : 'inPlace',
how: (uri instanceof vscode.Uri) ? 'action' : 'pallete'
@@ -286,7 +302,7 @@ function showSource(mdUri: vscode.Uri) {
const docUri = vscode.Uri.parse(mdUri.query);
for (const editor of vscode.window.visibleTextEditors) {
if (editor.document.uri.scheme === docUri.scheme && editor.document.uri.fsPath === docUri.fsPath) {
if (editor.document.uri.scheme === docUri.scheme && editor.document.uri.toString() === docUri.toString()) {
return vscode.window.showTextDocument(editor.document, editor.viewColumn);
}
}
+6 -6
View File
@@ -35,7 +35,7 @@ export class MarkdownEngine {
}
}
private async getEngine(): Promise<MarkdownIt> {
private async getEngine(resource: vscode.Uri): Promise<MarkdownIt> {
if (!this.md) {
const hljs = await import('highlight.js');
const mdnh = await import('markdown-it-named-headers');
@@ -66,10 +66,10 @@ export class MarkdownEngine {
this.addLinkValidator(this.md);
}
const config = vscode.workspace.getConfiguration('markdown');
const config = vscode.workspace.getConfiguration('markdown', resource);
this.md.set({
breaks: config.get('preview.breaks', false),
linkify: config.get('preview.linkify', true)
breaks: config.get<boolean>('preview.breaks', false),
linkify: config.get<boolean>('preview.linkify', true)
});
return this.md;
}
@@ -94,14 +94,14 @@ export class MarkdownEngine {
}
this.currentDocument = document;
this.firstLine = offset;
const engine = await this.getEngine();
const engine = await this.getEngine(document);
return engine.render(text);
}
public async parse(document: vscode.Uri, source: string): Promise<Token[]> {
const { text, offset } = this.stripFrontmatter(source);
this.currentDocument = document;
const engine = await this.getEngine();
const engine = await this.getEngine(document);
return engine.parse(text, {}).map(token => {
if (token.map) {
@@ -38,8 +38,8 @@ export function getMarkdownUri(uri: vscode.Uri) {
}
class MarkdownPreviewConfig {
public static getCurrentConfig() {
return new MarkdownPreviewConfig();
public static getConfigForResource(resource: vscode.Uri) {
return new MarkdownPreviewConfig(resource);
}
public readonly scrollBeyondLastLine: boolean;
@@ -56,9 +56,9 @@ class MarkdownPreviewConfig {
public readonly fontFamily: string | undefined;
public readonly styles: string[];
private constructor() {
const editorConfig = vscode.workspace.getConfiguration('editor');
const markdownConfig = vscode.workspace.getConfiguration('markdown');
private constructor(resource: vscode.Uri) {
const editorConfig = vscode.workspace.getConfiguration('editor', resource);
const markdownConfig = vscode.workspace.getConfiguration('markdown', resource);
const markdownEditorConfig = vscode.workspace.getConfiguration('[markdown]');
this.scrollBeyondLastLine = editorConfig.get<boolean>('scrollBeyondLastLine', false);
@@ -107,11 +107,41 @@ class MarkdownPreviewConfig {
[key: string]: any;
}
class PreviewConfigManager {
private previewConfigurationsForWorkspaces = new Map<string, MarkdownPreviewConfig>();
public loadAndCacheConfiguration(
resource: vscode.Uri
) {
const config = MarkdownPreviewConfig.getConfigForResource(resource);
this.previewConfigurationsForWorkspaces.set(this.getKey(resource), config);
return config;
}
public shouldUpdateConfiguration(
resource: vscode.Uri
): boolean {
const key = this.getKey(resource);
const currentConfig = this.previewConfigurationsForWorkspaces.get(key);
const newConfig = MarkdownPreviewConfig.getConfigForResource(resource);
return (!currentConfig || !currentConfig.isEqualTo(newConfig));
}
private getKey(
resource: vscode.Uri
): string {
const folder = vscode.workspace.getWorkspaceFolder(resource);
if (!folder) {
return '';
}
return folder.uri.toString();
}
}
export class MDDocumentContentProvider implements vscode.TextDocumentContentProvider {
private _onDidChange = new vscode.EventEmitter<vscode.Uri>();
private _waiting: boolean = false;
private config: MarkdownPreviewConfig;
private previewConfigurations = new PreviewConfigManager();
private extraStyles: Array<vscode.Uri> = [];
private extraScripts: Array<vscode.Uri> = [];
@@ -121,9 +151,7 @@ export class MDDocumentContentProvider implements vscode.TextDocumentContentProv
private context: vscode.ExtensionContext,
private cspArbiter: ContentSecurityPolicyArbiter,
private logger: Logger
) {
this.config = MarkdownPreviewConfig.getCurrentConfig();
}
) { }
public addScript(resource: vscode.Uri): void {
this.extraScripts.push(resource);
@@ -163,34 +191,34 @@ export class MDDocumentContentProvider implements vscode.TextDocumentContentProv
return vscode.Uri.file(path.join(path.dirname(resource.fsPath), href)).toString();
}
private computeCustomStyleSheetIncludes(uri: vscode.Uri): string {
if (this.config.styles && Array.isArray(this.config.styles)) {
return this.config.styles.map((style) => {
return `<link rel="stylesheet" class="code-user-style" data-source="${style.replace(/"/g, '&quot;')}" href="${this.fixHref(uri, style)}" type="text/css" media="screen">`;
private computeCustomStyleSheetIncludes(resource: vscode.Uri, config: MarkdownPreviewConfig): string {
if (config.styles && Array.isArray(config.styles)) {
return config.styles.map(style => {
return `<link rel="stylesheet" class="code-user-style" data-source="${style.replace(/"/g, '&quot;')}" href="${this.fixHref(resource, style)}" type="text/css" media="screen">`;
}).join('\n');
}
return '';
}
private getSettingsOverrideStyles(nonce: string): string {
private getSettingsOverrideStyles(nonce: string, config: MarkdownPreviewConfig): string {
return `<style nonce="${nonce}">
body {
${this.config.fontFamily ? `font-family: ${this.config.fontFamily};` : ''}
${isNaN(this.config.fontSize) ? '' : `font-size: ${this.config.fontSize}px;`}
${isNaN(this.config.lineHeight) ? '' : `line-height: ${this.config.lineHeight};`}
${config.fontFamily ? `font-family: ${config.fontFamily};` : ''}
${isNaN(config.fontSize) ? '' : `font-size: ${config.fontSize}px;`}
${isNaN(config.lineHeight) ? '' : `line-height: ${config.lineHeight};`}
}
</style>`;
}
private getStyles(resource: vscode.Uri, nonce: string): string {
private getStyles(resource: vscode.Uri, nonce: string, config: MarkdownPreviewConfig): string {
const baseStyles = [
this.getMediaPath('markdown.css'),
this.getMediaPath('tomorrow.css')
].concat(this.extraStyles.map(resource => resource.toString()));
return `${baseStyles.map(href => `<link rel="stylesheet" type="text/css" href="${href}">`).join('\n')}
${this.getSettingsOverrideStyles(nonce)}
${this.computeCustomStyleSheetIncludes(resource)}`;
${this.getSettingsOverrideStyles(nonce, config)}
${this.computeCustomStyleSheetIncludes(resource, config)}`;
}
private getScripts(nonce: string): string {
@@ -205,20 +233,20 @@ export class MDDocumentContentProvider implements vscode.TextDocumentContentProv
let initialLine: number | undefined = undefined;
const editor = vscode.window.activeTextEditor;
if (editor && editor.document.uri.fsPath === sourceUri.fsPath) {
if (editor && editor.document.uri.toString() === sourceUri.toString()) {
initialLine = editor.selection.active.line;
}
const document = await vscode.workspace.openTextDocument(sourceUri);
this.config = MarkdownPreviewConfig.getCurrentConfig();
const config = this.previewConfigurations.loadAndCacheConfiguration(sourceUri);
const initialData = {
previewUri: uri.toString(),
source: sourceUri.toString(),
line: initialLine,
scrollPreviewWithEditorSelection: this.config.scrollPreviewWithEditorSelection,
scrollEditorWithPreview: this.config.scrollEditorWithPreview,
doubleClickToSwitchToEditor: this.config.doubleClickToSwitchToEditor
scrollPreviewWithEditorSelection: config.scrollPreviewWithEditorSelection,
scrollEditorWithPreview: config.scrollEditorWithPreview,
doubleClickToSwitchToEditor: config.doubleClickToSwitchToEditor
};
this.logger.log('provideTextDocumentContent', initialData);
@@ -227,7 +255,7 @@ export class MDDocumentContentProvider implements vscode.TextDocumentContentProv
const nonce = new Date().getTime() + '' + new Date().getMilliseconds();
const csp = this.getCspForResource(sourceUri, nonce);
const body = await this.engine.render(sourceUri, this.config.previewFrontMatter === 'hide', document.getText());
const body = await this.engine.render(sourceUri, config.previewFrontMatter === 'hide', document.getText());
return `<!DOCTYPE html>
<html>
<head>
@@ -236,10 +264,10 @@ export class MDDocumentContentProvider implements vscode.TextDocumentContentProv
<meta id="vscode-markdown-preview-data" data-settings="${JSON.stringify(initialData).replace(/"/g, '&quot;')}" data-strings="${JSON.stringify(previewStrings).replace(/"/g, '&quot;')}">
<script src="${this.getMediaPath('csp.js')}" nonce="${nonce}"></script>
<script src="${this.getMediaPath('loading.js')}" nonce="${nonce}"></script>
${this.getStyles(sourceUri, nonce)}
${this.getStyles(sourceUri, nonce, config)}
<base href="${document.uri.toString(true)}">
</head>
<body class="vscode-body ${this.config.scrollBeyondLastLine ? 'scrollBeyondLastLine' : ''} ${this.config.wordWrap ? 'wordWrap' : ''} ${this.config.markEditorSelection ? 'showEditorSelection' : ''}">
<body class="vscode-body ${config.scrollBeyondLastLine ? 'scrollBeyondLastLine' : ''} ${config.wordWrap ? 'wordWrap' : ''} ${config.markEditorSelection ? 'showEditorSelection' : ''}">
${body}
<div class="code-line" data-line="${document.lineCount}"></div>
${this.getScripts(nonce)}
@@ -248,12 +276,11 @@ export class MDDocumentContentProvider implements vscode.TextDocumentContentProv
}
public updateConfiguration() {
const newConfig = MarkdownPreviewConfig.getCurrentConfig();
if (!this.config.isEqualTo(newConfig)) {
this.config = newConfig;
// update all generated md documents
for (const document of vscode.workspace.textDocuments) {
if (document.uri.scheme === 'markdown') {
// update all generated md documents
for (const document of vscode.workspace.textDocuments) {
if (document.uri.scheme === 'markdown') {
const sourceUri = vscode.Uri.parse(document.uri.query);
if (this.previewConfigurations.shouldUpdateConfiguration(sourceUri)) {
this.update(document.uri);
}
}
@@ -79,7 +79,7 @@ export class TableOfContentsProvider {
}
private static getHeaderText(header: string): string {
return header.replace(/^\s*#+\s*(.*?)\s*\1*$/, (_, word) => `${word.trim()}`);
return header.replace(/^\s*#+\s*(.*?)\s*#*$/, (_, word) => word.trim());
}
public static slugify(header: string): string {
+57 -42
View File
@@ -1,64 +1,69 @@
// @ts-check
var gulp = require('gulp');
var util = require("gulp-util");
var replace = require('gulp-replace');
var rename = require('gulp-rename');
const languages = [
{ name: 'css', identifiers: ['css', 'css.erb'], source: 'source.css' },
{ name: 'basic', identifiers: ['html', 'htm', 'shtml', 'xhtml', 'inc', 'tmpl', 'tpl'], source: 'text.html.basic' },
{ name: 'ini', identifiers: ['ini', 'conf'], source: 'source.ini' },
{ name: 'java', identifiers: ['java', 'bsh'], source: 'source.java' },
{ name: 'lua', identifiers: ['lua'], source: 'source.lua' },
{ name: 'makefile', identifiers: ['Makefile', 'makefile', 'GNUmakefile', 'OCamlMakefile'], source: 'source.makefile' },
{ name: 'perl', identifiers: ['perl', 'pl', 'pm', 'pod', 't', 'PL', 'psgi', 'vcl'], source: 'source.perl' },
{ name: 'r', identifiers: ['R', 'r', 's', 'S', 'Rprofile'], source: 'source.r' },
{ name: 'ruby', identifiers: ['ruby', 'rb', 'rbx', 'rjs', 'Rakefile', 'rake', 'cgi', 'fcgi', 'gemspec', 'irbrc', 'Capfile', 'ru', 'prawn', 'Cheffile', 'Gemfile', 'Guardfile', 'Hobofile', 'Vagrantfile', 'Appraisals', 'Rantfile', 'Berksfile', 'Berksfile.lock', 'Thorfile', 'Puppetfile'], source: 'source.ruby' },
{ name: 'css', language: 'css', identifiers: ['css', 'css.erb'], source: 'source.css' },
{ name: 'basic', language: 'html', identifiers: ['html', 'htm', 'shtml', 'xhtml', 'inc', 'tmpl', 'tpl'], source: 'text.html.basic' },
{ name: 'ini', language: 'ini', identifiers: ['ini', 'conf'], source: 'source.ini' },
{ name: 'java', language: 'java', identifiers: ['java', 'bsh'], source: 'source.java' },
{ name: 'lua', language: 'lua', identifiers: ['lua'], source: 'source.lua' },
{ name: 'makefile', language: 'makefile', identifiers: ['Makefile', 'makefile', 'GNUmakefile', 'OCamlMakefile'], source: 'source.makefile' },
{ name: 'perl', language: 'perl', identifiers: ['perl', 'pl', 'pm', 'pod', 't', 'PL', 'psgi', 'vcl'], source: 'source.perl' },
{ name: 'r', language: 'r', identifiers: ['R', 'r', 's', 'S', 'Rprofile'], source: 'source.r' },
{ name: 'ruby', language: 'ruby', identifiers: ['ruby', 'rb', 'rbx', 'rjs', 'Rakefile', 'rake', 'cgi', 'fcgi', 'gemspec', 'irbrc', 'Capfile', 'ru', 'prawn', 'Cheffile', 'Gemfile', 'Guardfile', 'Hobofile', 'Vagrantfile', 'Appraisals', 'Rantfile', 'Berksfile', 'Berksfile.lock', 'Thorfile', 'Puppetfile'], source: 'source.ruby' },
// Left to its own devices, the PHP grammar will match HTML as a combination of operators
// and constants. Therefore, HTML must take precedence over PHP in order to get proper
// syntax highlighting.
{ name: 'php', identifiers: ['php', 'php3', 'php4', 'php5', 'phpt', 'phtml', 'aw', 'ctp'], source: ['text.html.basic', 'text.html.php#language'] },
{ name: 'sql', identifiers: ['sql', 'ddl', 'dml'], source: 'source.sql' },
{ name: 'vs_net', identifiers: ['vb'], source: 'source.asp.vb.net' },
{ name: 'xml', identifiers: ['xml', 'xsd', 'tld', 'jsp', 'pt', 'cpt', 'dtml', 'rss', 'opml'], source: 'text.xml' },
{ name: 'xsl', identifiers: ['xsl', 'xslt'], source: 'text.xml.xsl' },
{ name: 'yaml', identifiers: ['yaml', 'yml'], source: 'source.yaml' },
{ name: 'dosbatch', identifiers: ['bat', 'batch'], source: 'source.dosbatch' },
{ name: 'clojure', identifiers: ['clj', 'cljs', 'clojure'], source: 'source.clojure' },
{ name: 'coffee', identifiers: ['coffee', 'Cakefile', 'coffee.erb'], source: 'source.coffee' },
{ name: 'c', identifiers: ['c', 'h'], source: 'source.c' },
{ name: 'cpp', identifiers: ['cpp', 'c\\+\\+', 'cxx'], source: 'source.cpp' },
{ name: 'diff', identifiers: ['patch', 'diff', 'rej'], source: 'source.diff' },
{ name: 'dockerfile', identifiers: ['dockerfile', 'Dockerfile'], source: 'source.dockerfile' },
{ name: 'php', language: 'php', identifiers: ['php', 'php3', 'php4', 'php5', 'phpt', 'phtml', 'aw', 'ctp'], source: ['text.html.basic', 'text.html.php#language'] },
{ name: 'sql', language: 'sql', identifiers: ['sql', 'ddl', 'dml'], source: 'source.sql' },
{ name: 'vs_net', language: 'vs_net', identifiers: ['vb'], source: 'source.asp.vb.net' },
{ name: 'xml', language: 'xml', identifiers: ['xml', 'xsd', 'tld', 'jsp', 'pt', 'cpt', 'dtml', 'rss', 'opml'], source: 'text.xml' },
{ name: 'xsl', language: 'xsl', identifiers: ['xsl', 'xslt'], source: 'text.xml.xsl' },
{ name: 'yaml', language: 'yaml', identifiers: ['yaml', 'yml'], source: 'source.yaml' },
{ name: 'dosbatch', language: 'dosbatch', identifiers: ['bat', 'batch'], source: 'source.dosbatch' },
{ name: 'clojure', language: 'clojure', identifiers: ['clj', 'cljs', 'clojure'], source: 'source.clojure' },
{ name: 'coffee', language: 'coffee', identifiers: ['coffee', 'Cakefile', 'coffee.erb'], source: 'source.coffee' },
{ name: 'c', language: 'c', identifiers: ['c', 'h'], source: 'source.c' },
{ name: 'cpp', language: 'cpp', identifiers: ['cpp', 'c\\+\\+', 'cxx'], source: 'source.cpp' },
{ name: 'diff', language: 'diff', identifiers: ['patch', 'diff', 'rej'], source: 'source.diff' },
{ name: 'dockerfile', language: 'dockerfile', identifiers: ['dockerfile', 'Dockerfile'], source: 'source.dockerfile' },
{ name: 'git_commit', identifiers: ['COMMIT_EDITMSG', 'MERGE_MSG'], source: 'text.git-commit' },
{ name: 'git_rebase', identifiers: ['git-rebase-todo'], source: 'text.git-rebase' },
{ name: 'go', identifiers: ['go', 'golang'], source: 'source.go' },
{ name: 'groovy', identifiers: ['groovy', 'gvy'], source: 'source.groovy' },
{ name: 'jade', identifiers: ['jade', 'pug'], source: 'text.jade' },
{ name: 'go', language: 'go', identifiers: ['go', 'golang'], source: 'source.go' },
{ name: 'groovy', language: 'groovy', identifiers: ['groovy', 'gvy'], source: 'source.groovy' },
{ name: 'jade', language: 'jade', identifiers: ['jade', 'pug'], source: 'text.jade' },
{ name: 'js', identifiers: ['js', 'jsx', 'javascript', 'es6', 'mjs'], source: 'source.js' },
{ name: 'js', language: 'javascript', identifiers: ['js', 'jsx', 'javascript', 'es6', 'mjs'], source: 'source.js' },
{ name: 'js_regexp', identifiers: ['regexp'], source: 'source.js.regexp' },
{ name: 'json', identifiers: ['json', 'sublime-settings', 'sublime-menu', 'sublime-keymap', 'sublime-mousemap', 'sublime-theme', 'sublime-build', 'sublime-project', 'sublime-completions'], source: 'source.json' },
{ name: 'less', identifiers: ['less'], source: 'source.css.less' },
{ name: 'objc', identifiers: ['objectivec', 'objective-c', 'mm', 'objc', 'obj-c', 'm', 'h'], source: 'source.objc' },
{ name: 'scss', identifiers: ['scss'], source: 'source.css.scss' },
{ name: 'json', language: 'json', identifiers: ['json', 'sublime-settings', 'sublime-menu', 'sublime-keymap', 'sublime-mousemap', 'sublime-theme', 'sublime-build', 'sublime-project', 'sublime-completions'], source: 'source.json' },
{ name: 'less', language: 'less', identifiers: ['less'], source: 'source.css.less' },
{ name: 'objc', language: 'objc', identifiers: ['objectivec', 'objective-c', 'mm', 'objc', 'obj-c', 'm', 'h'], source: 'source.objc' },
{ name: 'scss', language: 'scss', identifiers: ['scss'], source: 'source.css.scss' },
{ name: 'perl6', identifiers: ['perl6', 'p6', 'pl6', 'pm6', 'nqp'], source: 'source.perl.6' },
{ name: 'powershell', identifiers: ['powershell', 'ps1', 'psm1', 'psd1'], source: 'source.powershell' },
{ name: 'python', identifiers: ['python', 'py', 'py3', 'rpy', 'pyw', 'cpy', 'SConstruct', 'Sconstruct', 'sconstruct', 'SConscript', 'gyp', 'gypi'], source: 'source.python' },
{ name: 'perl6', language: 'perl6', identifiers: ['perl6', 'p6', 'pl6', 'pm6', 'nqp'], source: 'source.perl.6' },
{ name: 'powershell', language: 'powershell', identifiers: ['powershell', 'ps1', 'psm1', 'psd1'], source: 'source.powershell' },
{ name: 'python', language: 'python', identifiers: ['python', 'py', 'py3', 'rpy', 'pyw', 'cpy', 'SConstruct', 'Sconstruct', 'sconstruct', 'SConscript', 'gyp', 'gypi'], source: 'source.python' },
{ name: 'regexp_python', identifiers: ['re'], source: 'source.regexp.python' },
{ name: 'rust', identifiers: ['rust', 'rs'], source: 'source.rust' },
{ name: 'scala', identifiers: ['scala', 'sbt'], source: 'source.scala' },
{ name: 'shell', identifiers: ['shell', 'sh', 'bash', 'zsh', 'bashrc', 'bash_profile', 'bash_login', 'profile', 'bash_logout', '.textmate_init'], source: 'source.shell' },
{ name: 'ts', identifiers: ['typescript', 'ts'], source: 'source.ts' },
{ name: 'tsx', identifiers: ['tsx'], source: 'source.tsx' },
{ name: 'csharp', identifiers: ['cs', 'csharp', 'c#'], source: 'source.cs' },
{ name: 'fsharp', identifiers: ['fs', 'fsharp', 'f#'], source: 'source.fsharp' },
{ name: 'rust', language: 'rust', identifiers: ['rust', 'rs'], source: 'source.rust' },
{ name: 'scala', language: 'scala', identifiers: ['scala', 'sbt'], source: 'source.scala' },
{ name: 'shell', language: 'shellscript', identifiers: ['shell', 'sh', 'bash', 'zsh', 'bashrc', 'bash_profile', 'bash_login', 'profile', 'bash_logout', '.textmate_init'], source: 'source.shell' },
{ name: 'ts', language: 'typescript', identifiers: ['typescript', 'ts'], source: 'source.ts' },
{ name: 'tsx', language: 'typescriptreact', identifiers: ['tsx'], source: 'source.tsx' },
{ name: 'csharp', language: 'csharp', identifiers: ['cs', 'csharp', 'c#'], source: 'source.cs' },
{ name: 'fsharp', language: 'fsharp', identifiers: ['fs', 'fsharp', 'f#'], source: 'source.fsharp' },
];
const fencedCodeBlockDefinition = (name, identifiers, sourceScope) => {
const fencedCodeBlockDefinition = (name, identifiers, sourceScope, language) => {
if (!Array.isArray(sourceScope)) {
sourceScope = [sourceScope];
}
language = language || name
const scopes = sourceScope.map(scope =>
`<dict>
<key>include</key>
@@ -106,6 +111,8 @@ const fencedCodeBlockDefinition = (name, identifiers, sourceScope) => {
<string>(^|\\G)(\\s*)(.*)</string>
<key>while</key>
<string>(^|\\G)(?!\\s*([\`~]{3,})\\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.${language}</string>
<key>patterns</key>
<array>
${indent(4, scopes)}
@@ -129,7 +136,7 @@ const fencedCodeBlockInclude = (name) =>
const fencedCodeBlockDefinitions = () =>
languages
.map(language => fencedCodeBlockDefinition(language.name, language.identifiers, language.source))
.map(language => fencedCodeBlockDefinition(language.name, language.identifiers, language.source, language.language))
.join('\n');
@@ -147,3 +154,11 @@ gulp.task('default', function () {
.pipe(rename('markdown.tmLanguage'))
.pipe(gulp.dest('.'));
});
gulp.task('embedded', function () {
const out = {}
for (const lang of languages.filter(x => x.language)) {
out['meta.embedded.block.' +lang.language] = lang.language;
}
util.log(JSON.stringify(out, undefined, 4));
});
@@ -354,11 +354,11 @@
</dict>
</array>
<key>while</key>
<string>^\s*(?!&lt;/(script|style|pre)&gt;)</string>
<string>^(?!.*&lt;/(script|style|pre)&gt;)</string>
</dict>
</array>
<key>end</key>
<string>(?=&lt;/(script|style|pre)&gt;)</string>
<string>(?=.*&lt;/(script|style|pre)&gt;)</string>
</dict>
<dict>
<key>begin</key>
@@ -626,6 +626,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.css</string>
<key>patterns</key>
<array>
<dict>
@@ -677,6 +679,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.html</string>
<key>patterns</key>
<array>
<dict>
@@ -728,6 +732,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.ini</string>
<key>patterns</key>
<array>
<dict>
@@ -779,6 +785,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.java</string>
<key>patterns</key>
<array>
<dict>
@@ -830,6 +838,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.lua</string>
<key>patterns</key>
<array>
<dict>
@@ -881,6 +891,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.makefile</string>
<key>patterns</key>
<array>
<dict>
@@ -932,6 +944,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.perl</string>
<key>patterns</key>
<array>
<dict>
@@ -983,6 +997,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.r</string>
<key>patterns</key>
<array>
<dict>
@@ -1034,6 +1050,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.ruby</string>
<key>patterns</key>
<array>
<dict>
@@ -1085,6 +1103,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.php</string>
<key>patterns</key>
<array>
<dict>
@@ -1140,6 +1160,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.sql</string>
<key>patterns</key>
<array>
<dict>
@@ -1191,6 +1213,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.vs_net</string>
<key>patterns</key>
<array>
<dict>
@@ -1242,6 +1266,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.xml</string>
<key>patterns</key>
<array>
<dict>
@@ -1293,6 +1319,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.xsl</string>
<key>patterns</key>
<array>
<dict>
@@ -1344,6 +1372,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.yaml</string>
<key>patterns</key>
<array>
<dict>
@@ -1395,6 +1425,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.dosbatch</string>
<key>patterns</key>
<array>
<dict>
@@ -1446,6 +1478,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.clojure</string>
<key>patterns</key>
<array>
<dict>
@@ -1497,6 +1531,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.coffee</string>
<key>patterns</key>
<array>
<dict>
@@ -1548,6 +1584,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.c</string>
<key>patterns</key>
<array>
<dict>
@@ -1599,6 +1637,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.cpp</string>
<key>patterns</key>
<array>
<dict>
@@ -1650,6 +1690,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.diff</string>
<key>patterns</key>
<array>
<dict>
@@ -1701,6 +1743,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.dockerfile</string>
<key>patterns</key>
<array>
<dict>
@@ -1752,6 +1796,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.git_commit</string>
<key>patterns</key>
<array>
<dict>
@@ -1803,6 +1849,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.git_rebase</string>
<key>patterns</key>
<array>
<dict>
@@ -1854,6 +1902,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.go</string>
<key>patterns</key>
<array>
<dict>
@@ -1905,6 +1955,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.groovy</string>
<key>patterns</key>
<array>
<dict>
@@ -1956,6 +2008,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.jade</string>
<key>patterns</key>
<array>
<dict>
@@ -2007,6 +2061,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.javascript</string>
<key>patterns</key>
<array>
<dict>
@@ -2058,6 +2114,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.js_regexp</string>
<key>patterns</key>
<array>
<dict>
@@ -2109,6 +2167,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.json</string>
<key>patterns</key>
<array>
<dict>
@@ -2160,6 +2220,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.less</string>
<key>patterns</key>
<array>
<dict>
@@ -2211,6 +2273,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.objc</string>
<key>patterns</key>
<array>
<dict>
@@ -2262,6 +2326,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.scss</string>
<key>patterns</key>
<array>
<dict>
@@ -2313,6 +2379,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.perl6</string>
<key>patterns</key>
<array>
<dict>
@@ -2364,6 +2432,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.powershell</string>
<key>patterns</key>
<array>
<dict>
@@ -2415,6 +2485,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.python</string>
<key>patterns</key>
<array>
<dict>
@@ -2466,6 +2538,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.regexp_python</string>
<key>patterns</key>
<array>
<dict>
@@ -2517,6 +2591,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.rust</string>
<key>patterns</key>
<array>
<dict>
@@ -2568,6 +2644,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.scala</string>
<key>patterns</key>
<array>
<dict>
@@ -2619,6 +2697,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.shellscript</string>
<key>patterns</key>
<array>
<dict>
@@ -2670,6 +2750,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.typescript</string>
<key>patterns</key>
<array>
<dict>
@@ -2721,6 +2803,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.typescriptreact</string>
<key>patterns</key>
<array>
<dict>
@@ -2772,6 +2856,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.csharp</string>
<key>patterns</key>
<array>
<dict>
@@ -2823,6 +2909,8 @@
<string>(^|\G)(\s*)(.*)</string>
<key>while</key>
<string>(^|\G)(?!\s*([`~]{3,})\s*$)</string>
<key>contentName</key>
<string>meta.embedded.block.fsharp</string>
<key>patterns</key>
<array>
<dict>
@@ -3597,12 +3685,14 @@
<key>match</key>
<string>(`+)([^`]|(?!(?&lt;!`)\1(?!`))`)*+(\1)</string>
<key>name</key>
<string>markup.inline.raw.markdown</string>
<string>markup.inline.raw.string.markdown</string>
</dict>
</dict>
</dict>
<key>frontMatter</key>
<dict>
<key>contentName</key>
<string>meta.embedded.block.frontmatter</string>
<key>begin</key>
<string>\A-{3}\s*$</string>
<key>while</key>
@@ -179,11 +179,11 @@
</dict>
</array>
<key>while</key>
<string>^\s*(?!&lt;/(script|style|pre)&gt;)</string>
<string>^(?!.*&lt;/(script|style|pre)&gt;)</string>
</dict>
</array>
<key>end</key>
<string>(?=&lt;/(script|style|pre)&gt;)</string>
<string>(?=.*&lt;/(script|style|pre)&gt;)</string>
</dict>
<dict>
<key>begin</key>
@@ -1175,12 +1175,14 @@
<key>match</key>
<string>(`+)([^`]|(?!(?&lt;!`)\1(?!`))`)*+(\1)</string>
<key>name</key>
<string>markup.inline.raw.markdown</string>
<string>markup.inline.raw.string.markdown</string>
</dict>
</dict>
</dict>
<key>frontMatter</key>
<dict>
<key>contentName</key>
<string>meta.embedded.block.frontmatter</string>
<key>begin</key>
<string>\A-{3}\s*$</string>
<key>while</key>
@@ -0,0 +1,13 @@
# h
<pre><code>
# a
</code></pre>
# h
<pre>
# a
a</pre>
# h
@@ -0,0 +1,332 @@
[
{
"c": "#",
"t": "text.html.markdown markup.heading.markdown punctuation.definition.heading.markdown",
"r": {
"dark_plus": "markup.heading: #569CD6",
"light_plus": "markup.heading: #800000",
"dark_vs": "markup.heading: #569CD6",
"light_vs": "markup.heading: #800000",
"hc_black": "markup.heading: #6796E6"
}
},
{
"c": " ",
"t": "text.html.markdown markup.heading.markdown",
"r": {
"dark_plus": "markup.heading: #569CD6",
"light_plus": "markup.heading: #800000",
"dark_vs": "markup.heading: #569CD6",
"light_vs": "markup.heading: #800000",
"hc_black": "markup.heading: #6796E6"
}
},
{
"c": "h",
"t": "text.html.markdown markup.heading.markdown entity.name.section.markdown",
"r": {
"dark_plus": "markup.heading: #569CD6",
"light_plus": "markup.heading: #800000",
"dark_vs": "markup.heading: #569CD6",
"light_vs": "markup.heading: #800000",
"hc_black": "markup.heading: #6796E6"
}
},
{
"c": "<",
"t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.begin.html",
"r": {
"dark_plus": "punctuation.definition.tag: #808080",
"light_plus": "punctuation.definition.tag: #800000",
"dark_vs": "punctuation.definition.tag: #808080",
"light_vs": "punctuation.definition.tag: #800000",
"hc_black": "punctuation.definition.tag: #808080"
}
},
{
"c": "pre",
"t": "text.html.markdown meta.tag.block.any.html entity.name.tag.block.any.html",
"r": {
"dark_plus": "entity.name.tag: #569CD6",
"light_plus": "entity.name.tag: #800000",
"dark_vs": "entity.name.tag: #569CD6",
"light_vs": "entity.name.tag: #800000",
"hc_black": "entity.name.tag: #569CD6"
}
},
{
"c": ">",
"t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.end.html",
"r": {
"dark_plus": "punctuation.definition.tag: #808080",
"light_plus": "punctuation.definition.tag: #800000",
"dark_vs": "punctuation.definition.tag: #808080",
"light_vs": "punctuation.definition.tag: #800000",
"hc_black": "punctuation.definition.tag: #808080"
}
},
{
"c": "<",
"t": "text.html.markdown meta.tag.inline.any.html punctuation.definition.tag.begin.html",
"r": {
"dark_plus": "punctuation.definition.tag: #808080",
"light_plus": "punctuation.definition.tag: #800000",
"dark_vs": "punctuation.definition.tag: #808080",
"light_vs": "punctuation.definition.tag: #800000",
"hc_black": "punctuation.definition.tag: #808080"
}
},
{
"c": "code",
"t": "text.html.markdown meta.tag.inline.any.html entity.name.tag.inline.any.html",
"r": {
"dark_plus": "entity.name.tag: #569CD6",
"light_plus": "entity.name.tag: #800000",
"dark_vs": "entity.name.tag: #569CD6",
"light_vs": "entity.name.tag: #800000",
"hc_black": "entity.name.tag: #569CD6"
}
},
{
"c": ">",
"t": "text.html.markdown meta.tag.inline.any.html punctuation.definition.tag.end.html",
"r": {
"dark_plus": "punctuation.definition.tag: #808080",
"light_plus": "punctuation.definition.tag: #800000",
"dark_vs": "punctuation.definition.tag: #808080",
"light_vs": "punctuation.definition.tag: #800000",
"hc_black": "punctuation.definition.tag: #808080"
}
},
{
"c": "# a",
"t": "text.html.markdown",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "</",
"t": "text.html.markdown meta.paragraph.markdown meta.tag.inline.any.html punctuation.definition.tag.begin.html",
"r": {
"dark_plus": "punctuation.definition.tag: #808080",
"light_plus": "punctuation.definition.tag: #800000",
"dark_vs": "punctuation.definition.tag: #808080",
"light_vs": "punctuation.definition.tag: #800000",
"hc_black": "punctuation.definition.tag: #808080"
}
},
{
"c": "code",
"t": "text.html.markdown meta.paragraph.markdown meta.tag.inline.any.html entity.name.tag.inline.any.html",
"r": {
"dark_plus": "entity.name.tag: #569CD6",
"light_plus": "entity.name.tag: #800000",
"dark_vs": "entity.name.tag: #569CD6",
"light_vs": "entity.name.tag: #800000",
"hc_black": "entity.name.tag: #569CD6"
}
},
{
"c": ">",
"t": "text.html.markdown meta.paragraph.markdown meta.tag.inline.any.html punctuation.definition.tag.end.html",
"r": {
"dark_plus": "punctuation.definition.tag: #808080",
"light_plus": "punctuation.definition.tag: #800000",
"dark_vs": "punctuation.definition.tag: #808080",
"light_vs": "punctuation.definition.tag: #800000",
"hc_black": "punctuation.definition.tag: #808080"
}
},
{
"c": "</",
"t": "text.html.markdown meta.paragraph.markdown meta.tag.block.any.html punctuation.definition.tag.begin.html",
"r": {
"dark_plus": "punctuation.definition.tag: #808080",
"light_plus": "punctuation.definition.tag: #800000",
"dark_vs": "punctuation.definition.tag: #808080",
"light_vs": "punctuation.definition.tag: #800000",
"hc_black": "punctuation.definition.tag: #808080"
}
},
{
"c": "pre",
"t": "text.html.markdown meta.paragraph.markdown meta.tag.block.any.html entity.name.tag.block.any.html",
"r": {
"dark_plus": "entity.name.tag: #569CD6",
"light_plus": "entity.name.tag: #800000",
"dark_vs": "entity.name.tag: #569CD6",
"light_vs": "entity.name.tag: #800000",
"hc_black": "entity.name.tag: #569CD6"
}
},
{
"c": ">",
"t": "text.html.markdown meta.paragraph.markdown meta.tag.block.any.html punctuation.definition.tag.end.html",
"r": {
"dark_plus": "punctuation.definition.tag: #808080",
"light_plus": "punctuation.definition.tag: #800000",
"dark_vs": "punctuation.definition.tag: #808080",
"light_vs": "punctuation.definition.tag: #800000",
"hc_black": "punctuation.definition.tag: #808080"
}
},
{
"c": "#",
"t": "text.html.markdown markup.heading.markdown punctuation.definition.heading.markdown",
"r": {
"dark_plus": "markup.heading: #569CD6",
"light_plus": "markup.heading: #800000",
"dark_vs": "markup.heading: #569CD6",
"light_vs": "markup.heading: #800000",
"hc_black": "markup.heading: #6796E6"
}
},
{
"c": " ",
"t": "text.html.markdown markup.heading.markdown",
"r": {
"dark_plus": "markup.heading: #569CD6",
"light_plus": "markup.heading: #800000",
"dark_vs": "markup.heading: #569CD6",
"light_vs": "markup.heading: #800000",
"hc_black": "markup.heading: #6796E6"
}
},
{
"c": "h",
"t": "text.html.markdown markup.heading.markdown entity.name.section.markdown",
"r": {
"dark_plus": "markup.heading: #569CD6",
"light_plus": "markup.heading: #800000",
"dark_vs": "markup.heading: #569CD6",
"light_vs": "markup.heading: #800000",
"hc_black": "markup.heading: #6796E6"
}
},
{
"c": "<",
"t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.begin.html",
"r": {
"dark_plus": "punctuation.definition.tag: #808080",
"light_plus": "punctuation.definition.tag: #800000",
"dark_vs": "punctuation.definition.tag: #808080",
"light_vs": "punctuation.definition.tag: #800000",
"hc_black": "punctuation.definition.tag: #808080"
}
},
{
"c": "pre",
"t": "text.html.markdown meta.tag.block.any.html entity.name.tag.block.any.html",
"r": {
"dark_plus": "entity.name.tag: #569CD6",
"light_plus": "entity.name.tag: #800000",
"dark_vs": "entity.name.tag: #569CD6",
"light_vs": "entity.name.tag: #800000",
"hc_black": "entity.name.tag: #569CD6"
}
},
{
"c": ">",
"t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.end.html",
"r": {
"dark_plus": "punctuation.definition.tag: #808080",
"light_plus": "punctuation.definition.tag: #800000",
"dark_vs": "punctuation.definition.tag: #808080",
"light_vs": "punctuation.definition.tag: #800000",
"hc_black": "punctuation.definition.tag: #808080"
}
},
{
"c": "# a",
"t": "text.html.markdown",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "a",
"t": "text.html.markdown meta.paragraph.markdown",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "</",
"t": "text.html.markdown meta.paragraph.markdown meta.tag.block.any.html punctuation.definition.tag.begin.html",
"r": {
"dark_plus": "punctuation.definition.tag: #808080",
"light_plus": "punctuation.definition.tag: #800000",
"dark_vs": "punctuation.definition.tag: #808080",
"light_vs": "punctuation.definition.tag: #800000",
"hc_black": "punctuation.definition.tag: #808080"
}
},
{
"c": "pre",
"t": "text.html.markdown meta.paragraph.markdown meta.tag.block.any.html entity.name.tag.block.any.html",
"r": {
"dark_plus": "entity.name.tag: #569CD6",
"light_plus": "entity.name.tag: #800000",
"dark_vs": "entity.name.tag: #569CD6",
"light_vs": "entity.name.tag: #800000",
"hc_black": "entity.name.tag: #569CD6"
}
},
{
"c": ">",
"t": "text.html.markdown meta.paragraph.markdown meta.tag.block.any.html punctuation.definition.tag.end.html",
"r": {
"dark_plus": "punctuation.definition.tag: #808080",
"light_plus": "punctuation.definition.tag: #800000",
"dark_vs": "punctuation.definition.tag: #808080",
"light_vs": "punctuation.definition.tag: #800000",
"hc_black": "punctuation.definition.tag: #808080"
}
},
{
"c": "#",
"t": "text.html.markdown markup.heading.markdown punctuation.definition.heading.markdown",
"r": {
"dark_plus": "markup.heading: #569CD6",
"light_plus": "markup.heading: #800000",
"dark_vs": "markup.heading: #569CD6",
"light_vs": "markup.heading: #800000",
"hc_black": "markup.heading: #6796E6"
}
},
{
"c": " ",
"t": "text.html.markdown markup.heading.markdown",
"r": {
"dark_plus": "markup.heading: #569CD6",
"light_plus": "markup.heading: #800000",
"dark_vs": "markup.heading: #569CD6",
"light_vs": "markup.heading: #800000",
"hc_black": "markup.heading: #6796E6"
}
},
{
"c": "h",
"t": "text.html.markdown markup.heading.markdown entity.name.section.markdown",
"r": {
"dark_plus": "markup.heading: #569CD6",
"light_plus": "markup.heading: #800000",
"dark_vs": "markup.heading: #569CD6",
"light_vs": "markup.heading: #800000",
"hc_black": "markup.heading: #6796E6"
}
}
]
@@ -652,11 +652,11 @@
"c": " ",
"t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.html",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
"dark_plus": "meta.embedded: #D4D4D4",
"light_plus": "meta.embedded: #000000",
"dark_vs": "meta.embedded: #D4D4D4",
"light_vs": "meta.embedded: #000000",
"hc_black": "meta.embedded: #FFFFFF"
}
},
{
@@ -674,11 +674,11 @@
"c": "=",
"t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.html",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
"dark_plus": "meta.embedded: #D4D4D4",
"light_plus": "meta.embedded: #000000",
"dark_vs": "meta.embedded: #D4D4D4",
"light_vs": "meta.embedded: #000000",
"hc_black": "meta.embedded: #FFFFFF"
}
},
{
@@ -729,22 +729,22 @@
"c": " function( x: int ) { return x*x; }",
"t": "text.html.markdown meta.embedded.block.html source.unknown",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
"dark_plus": "meta.embedded: #D4D4D4",
"light_plus": "meta.embedded: #000000",
"dark_vs": "meta.embedded: #D4D4D4",
"light_vs": "meta.embedded: #000000",
"hc_black": "meta.embedded: #FFFFFF"
}
},
{
"c": " ",
"t": "text.html.markdown meta.embedded.block.html source.unknown",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
"dark_plus": "meta.embedded: #D4D4D4",
"light_plus": "meta.embedded: #000000",
"dark_vs": "meta.embedded: #D4D4D4",
"light_vs": "meta.embedded: #000000",
"hc_black": "meta.embedded: #FFFFFF"
}
},
{
@@ -1026,11 +1026,11 @@
"c": " ",
"t": "text.html.markdown meta.embedded.block.html source.css",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
"dark_plus": "meta.embedded: #D4D4D4",
"light_plus": "meta.embedded: #000000",
"dark_vs": "meta.embedded: #D4D4D4",
"light_vs": "meta.embedded: #000000",
"hc_black": "meta.embedded: #FFFFFF"
}
},
{
@@ -1048,33 +1048,33 @@
"c": " ",
"t": "text.html.markdown meta.embedded.block.html source.css",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
"dark_plus": "meta.embedded: #D4D4D4",
"light_plus": "meta.embedded: #000000",
"dark_vs": "meta.embedded: #D4D4D4",
"light_vs": "meta.embedded: #000000",
"hc_black": "meta.embedded: #FFFFFF"
}
},
{
"c": "{",
"t": "text.html.markdown meta.embedded.block.html source.css meta.property-list.css punctuation.section.property-list.begin.bracket.curly.css",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
"dark_plus": "meta.embedded: #D4D4D4",
"light_plus": "meta.embedded: #000000",
"dark_vs": "meta.embedded: #D4D4D4",
"light_vs": "meta.embedded: #000000",
"hc_black": "meta.embedded: #FFFFFF"
}
},
{
"c": " ",
"t": "text.html.markdown meta.embedded.block.html source.css meta.property-list.css",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
"dark_plus": "meta.embedded: #D4D4D4",
"light_plus": "meta.embedded: #000000",
"dark_vs": "meta.embedded: #D4D4D4",
"light_vs": "meta.embedded: #000000",
"hc_black": "meta.embedded: #FFFFFF"
}
},
{
@@ -1092,22 +1092,22 @@
"c": ":",
"t": "text.html.markdown meta.embedded.block.html source.css meta.property-list.css punctuation.separator.key-value.css",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
"dark_plus": "meta.embedded: #D4D4D4",
"light_plus": "meta.embedded: #000000",
"dark_vs": "meta.embedded: #D4D4D4",
"light_vs": "meta.embedded: #000000",
"hc_black": "meta.embedded: #FFFFFF"
}
},
{
"c": " ",
"t": "text.html.markdown meta.embedded.block.html source.css meta.property-list.css",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
"dark_plus": "meta.embedded: #D4D4D4",
"light_plus": "meta.embedded: #000000",
"dark_vs": "meta.embedded: #D4D4D4",
"light_vs": "meta.embedded: #000000",
"hc_black": "meta.embedded: #FFFFFF"
}
},
{
@@ -1147,33 +1147,33 @@
"c": " ",
"t": "text.html.markdown meta.embedded.block.html source.css meta.property-list.css",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
"dark_plus": "meta.embedded: #D4D4D4",
"light_plus": "meta.embedded: #000000",
"dark_vs": "meta.embedded: #D4D4D4",
"light_vs": "meta.embedded: #000000",
"hc_black": "meta.embedded: #FFFFFF"
}
},
{
"c": "}",
"t": "text.html.markdown meta.embedded.block.html source.css meta.property-list.css punctuation.section.property-list.end.bracket.curly.css",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
"dark_plus": "meta.embedded: #D4D4D4",
"light_plus": "meta.embedded: #000000",
"dark_vs": "meta.embedded: #D4D4D4",
"light_vs": "meta.embedded: #000000",
"hc_black": "meta.embedded: #FFFFFF"
}
},
{
"c": " ",
"t": "text.html.markdown meta.embedded.block.html source.css",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
"dark_plus": "meta.embedded: #D4D4D4",
"light_plus": "meta.embedded: #000000",
"dark_vs": "meta.embedded: #D4D4D4",
"light_vs": "meta.embedded: #000000",
"hc_black": "meta.embedded: #FFFFFF"
}
},
{
@@ -1541,7 +1541,7 @@
},
{
"c": "`",
"t": "text.html.markdown meta.paragraph.markdown markup.inline.raw.markdown punctuation.definition.raw.markdown",
"t": "text.html.markdown meta.paragraph.markdown markup.inline.raw.string.markdown punctuation.definition.raw.markdown",
"r": {
"dark_plus": "markup.inline.raw: #CE9178",
"light_plus": "markup.inline.raw: #800000",
@@ -1552,7 +1552,7 @@
},
{
"c": "code()",
"t": "text.html.markdown meta.paragraph.markdown markup.inline.raw.markdown",
"t": "text.html.markdown meta.paragraph.markdown markup.inline.raw.string.markdown",
"r": {
"dark_plus": "markup.inline.raw: #CE9178",
"light_plus": "markup.inline.raw: #800000",
@@ -1563,7 +1563,7 @@
},
{
"c": "`",
"t": "text.html.markdown meta.paragraph.markdown markup.inline.raw.markdown punctuation.definition.raw.markdown",
"t": "text.html.markdown meta.paragraph.markdown markup.inline.raw.string.markdown punctuation.definition.raw.markdown",
"r": {
"dark_plus": "markup.inline.raw: #CE9178",
"light_plus": "markup.inline.raw: #800000",
+1 -1
View File
@@ -88,6 +88,6 @@
"vscode-nls": "^2.0.2"
},
"devDependencies": {
"@types/node": "^7.0.4"
"@types/node": "8.0.33"
}
}
@@ -10,14 +10,14 @@
{
"name": "balanced-match",
"repositoryURL": "https://github.com/juliangruber/balanced-match",
"version": "0.4.2",
"version": "1.0.0",
"license": "MIT",
"isProd": true
},
{
"name": "brace-expansion",
"repositoryURL": "https://github.com/juliangruber/brace-expansion",
"version": "1.1.6",
"version": "1.1.8",
"license": "MIT",
"isProd": true
},
@@ -31,14 +31,14 @@
{
"name": "debug",
"repositoryURL": "https://github.com/visionmedia/debug",
"version": "2.3.3",
"version": "2.6.8",
"license": "MIT",
"isProd": true
},
{
"name": "extend",
"repositoryURL": "https://github.com/justmoon/node-extend",
"version": "3.0.0",
"version": "3.0.1",
"license": "MIT",
"isProd": true
},
@@ -52,7 +52,7 @@
{
"name": "glob",
"repositoryURL": "https://github.com/isaacs/node-glob",
"version": "7.1.1",
"version": "7.1.2",
"license": "ISC",
"isProd": true
},
@@ -87,14 +87,14 @@
{
"name": "minimatch",
"repositoryURL": "https://github.com/isaacs/minimatch",
"version": "3.0.3",
"version": "3.0.4",
"license": "ISC",
"isProd": true
},
{
"name": "ms",
"repositoryURL": "https://github.com/rauchg/ms.js",
"version": "0.7.2",
"version": "2.0.0",
"license": "MIT",
"isProd": true
},
@@ -0,0 +1,447 @@
/*---------------------------------------------------------------------------------------------
* 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 request from 'request';
import * as nls from 'vscode-nls';
import * as vscode from 'vscode';
import * as url from 'url';
import {
Arguments,
AzureAccount,
AzureAccountProviderMetadata,
AzureAccountSecurityTokenCollection,
Tenant
} from './interfaces';
import TokenCache from './tokenCache';
const localize = nls.loadMessageBundle();
export class AzureAccountProvider implements data.AccountProvider {
// CONSTANTS ///////////////////////////////////////////////////////////
private static WorkSchoolAccountType: string = 'work_school';
private static MicrosoftAccountType: string = 'microsoft';
private static AadCommonTenant: string = 'common';
// MEMBER VARIABLES ////////////////////////////////////////////////////
private _args: Arguments;
private _autoOAuthCancelled: boolean;
private _commonAuthorityUrl: string;
private _inProgressAutoOAuth: InProgressAutoOAuth;
private _isInitialized: boolean;
constructor(private _metadata: AzureAccountProviderMetadata, private _tokenCache: TokenCache) {
this._args = {
host: this._metadata.settings.host,
clientId: this._metadata.settings.clientId
};
this._autoOAuthCancelled = false;
this._inProgressAutoOAuth = null;
this._isInitialized = false;
this._commonAuthorityUrl = url.resolve(this._metadata.settings.host, AzureAccountProvider.AadCommonTenant);
}
// PUBLIC METHODS //////////////////////////////////////////////////////
public autoOAuthCancelled(): Thenable<void> {
return this.doIfInitialized(() => this.cancelAutoOAuth());
}
/**
* Clears all tokens that belong to the given account from the token cache
* @param {"data".AccountKey} accountKey Key identifying the account to delete tokens for
* @returns {Thenable<void>} Promise to clear requested tokens from the token cache
*/
public clear(accountKey: data.AccountKey): Thenable<void> {
return this.doIfInitialized(() => this.clearAccountTokens(accountKey));
}
/**
* Clears the entire token cache. Invoked by command palette action.
* @returns {Thenable<void>} Promise to clear the token cache
*/
public clearTokenCache(): Thenable<void> {
return this._tokenCache.clear();
}
public getSecurityToken(account: AzureAccount): Thenable<AzureAccountSecurityTokenCollection> {
return this.doIfInitialized(() => this.getAccessTokens(account));
}
public initialize(restoredAccounts: data.Account[]): Thenable<data.Account[]> {
let self = this;
let rehydrationTasks: Thenable<data.Account>[] = [];
for (let account of restoredAccounts) {
// Purge any invalid accounts
if (!account) {
continue;
}
// Refresh the contextual logo based on whether the account is a MS account
account.displayInfo.accountType = account.properties.isMsAccount
? AzureAccountProvider.MicrosoftAccountType
: AzureAccountProvider.WorkSchoolAccountType;
// Attempt to get fresh tokens. If this fails then the account is stale.
// NOTE: Based on ADAL implementation, getting tokens should use the refresh token if necessary
let task = this.getAccessTokens(account)
.then(
() => {
return account;
},
() => {
account.isStale = true;
return account;
}
);
rehydrationTasks.push(task);
}
// Collect the rehydration tasks and mark the provider as initialized
return Promise.all(rehydrationTasks)
.then(accounts => {
self._isInitialized = true;
return accounts;
});
}
public prompt(): Thenable<AzureAccount> {
return this.doIfInitialized(() => this.signIn(true));
}
public refresh(account: AzureAccount): Thenable<AzureAccount> {
return this.doIfInitialized(() => this.signIn(false));
}
// PRIVATE METHODS /////////////////////////////////////////////////////
private cancelAutoOAuth(): Thenable<void> {
let self = this;
if (!this._inProgressAutoOAuth) {
console.warn('Attempted to cancel auto OAuth when auto OAuth is not in progress!');
return Promise.resolve();
}
// Indicate oauth was cancelled by the user
let inProgress = self._inProgressAutoOAuth;
self._autoOAuthCancelled = true;
self._inProgressAutoOAuth = null;
// Use the auth context that was originally used to open the polling request, and cancel the polling
let context = inProgress.context;
context.cancelRequestToGetTokenWithDeviceCode(inProgress.userCodeInfo, err => {
// Callback is only called in failure scenarios.
if (err) {
console.warn(`Error while cancelling auto OAuth: ${err}`);
}
});
return Promise.resolve();
}
private clearAccountTokens(accountKey: data.AccountKey): Thenable<void> {
// Put together a query to look up any tokens associated with the account key
let query = <adal.TokenResponse>{ userId: accountKey.accountId };
// 1) Look up the tokens associated with the query
// 2) Remove them
return this._tokenCache.findThenable(query)
.then(results => this._tokenCache.removeThenable(results));
}
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;
let accessTokenPromises: Thenable<void>[] = [];
let tokenCollection: AzureAccountSecurityTokenCollection = {};
for (let tenant of account.properties.tenants) {
let promise = new Promise<void>((resolve, reject) => {
let authorityUrl = url.resolve(self._metadata.settings.host, tenant.id);
let context = new adal.AuthenticationContext(authorityUrl, null, self._tokenCache);
context.acquireToken(
self._metadata.settings.armResource.id,
tenant.userId,
self._metadata.settings.clientId,
(error: Error, response: adal.TokenResponse | adal.ErrorResponse) => {
// Handle errors first
if (error) {
// TODO: We'll assume for now that the account is stale, though that might not be accurate
account.isStale = true;
data.accounts.accountUpdated(account);
reject(error);
return;
}
// We know that the response was not an error
let tokenResponse = <adal.TokenResponse>response;
// Generate a token object and add it to the collection
tokenCollection[tenant.id] = {
expiresOn: tokenResponse.expiresOn,
resource: tokenResponse.resource,
token: tokenResponse.accessToken,
tokenType: tokenResponse.tokenType
};
resolve();
}
);
});
accessTokenPromises.push(promise);
}
// Wait until all the tokens have been acquired then return the collection
return Promise.all(accessTokenPromises)
.then(() => tokenCollection);
}
private getDeviceLoginUserCode(): Thenable<InProgressAutoOAuth> {
let self = this;
// Create authentication context and acquire user code
return new Promise<InProgressAutoOAuth>((resolve, reject) => {
let context = new adal.AuthenticationContext(self._commonAuthorityUrl, null, self._tokenCache);
context.acquireUserCode(self._metadata.settings.signInResourceId, self._metadata.settings.clientId, vscode.env.language,
(err, response) => {
if (err) {
reject(err);
} else {
let result: InProgressAutoOAuth = {
context: context,
userCodeInfo: response
};
resolve(result);
}
}
);
});
}
private getDeviceLoginToken(oAuth: InProgressAutoOAuth, isAddAccount: boolean): Thenable<adal.TokenResponse> {
let self = this;
// 1) Open the auto OAuth dialog
// 2) Begin the acquiring token polling
// 3) When that completes via callback, close the auto oauth
let title = isAddAccount ?
localize('addAccount', 'Add {0} account', self._metadata.displayName) :
localize('refreshAccount', 'Refresh {0} account', self._metadata.displayName);
return data.accounts.beginAutoOAuthDeviceCode(self._metadata.id, title, oAuth.userCodeInfo.message, oAuth.userCodeInfo.userCode, oAuth.userCodeInfo.verificationUrl)
.then(() => {
return new Promise<adal.TokenResponse>((resolve, reject) => {
let context = oAuth.context;
context.acquireTokenWithDeviceCode(self._metadata.settings.signInResourceId, self._metadata.settings.clientId, oAuth.userCodeInfo,
(err, response) => {
if (err) {
if (self._autoOAuthCancelled) {
// Auto OAuth was cancelled by the user, indicate this with the error we return
reject(<data.UserCancelledSignInError>{ userCancelledSignIn: true });
} else {
// Auto OAuth failed for some other reason
data.accounts.endAutoOAuthDeviceCode();
reject(err);
}
} else {
data.accounts.endAutoOAuthDeviceCode();
resolve(<adal.TokenResponse>response);
}
}
);
});
});
}
private getTenants(userId: string, homeTenant: string): Thenable<Tenant[]> {
let self = this;
// 1) Get a token we can use for looking up the tenant IDs
// 2) Send a request to the ARM endpoint (the root management API) to get the list of tenant IDs
// 3) For all the tenants
// b) Get a token we can use for the AAD Graph API
// a) Get the display name of the tenant
// c) create a tenant object
// 4) Sort to make sure the "home tenant" is the first tenant on the list
return this.getToken(userId, AzureAccountProvider.AadCommonTenant, this._metadata.settings.armResource.id)
.then((armToken: adal.TokenResponse) => {
let tenantUri = url.resolve(self._metadata.settings.armResource.endpoint, 'tenants?api-version=2015-01-01');
return self.makeWebRequest(armToken, tenantUri);
})
.then((tenantResponse: any[]) => {
let promises: Thenable<Tenant>[] = tenantResponse.map(value => {
return self.getToken(userId, value.tenantId, self._metadata.settings.graphResource.id)
.then((graphToken: adal.TokenResponse) => {
let tenantDetailsUri = url.resolve(self._metadata.settings.graphResource.endpoint, value.tenantId + '/');
tenantDetailsUri = url.resolve(tenantDetailsUri, 'tenantDetails?api-version=2013-04-05');
return self.makeWebRequest(graphToken, tenantDetailsUri);
})
.then((tenantDetails: any) => {
return <Tenant>{
id: value.tenantId,
userId: userId,
displayName: tenantDetails.length && tenantDetails[0].displayName
? tenantDetails[0].displayName
: localize('azureWorkAccountDisplayName', 'Work or school account')
};
});
});
return Promise.all(promises);
})
.then((tenants: Tenant[]) => {
let homeTenantIndex = tenants.findIndex(tenant => tenant.id === homeTenant);
if (homeTenantIndex >= 0) {
let homeTenant = tenants.splice(homeTenantIndex, 1);
tenants.unshift(homeTenant[0]);
}
return tenants;
});
}
/**
* Retrieves a token for the given user ID for the specific tenant ID. If the token can, it
* will be retrieved from the cache as per the ADAL API. AFAIK, the ADAL API will also utilize
* the refresh token if there aren't any unexpired tokens to use.
* @param {string} userId ID of the user to get a token for
* @param {string} tenantId Tenant to get the token for
* @param {string} resourceId ID of the resource the token will be good for
* @returns {Thenable<TokenResponse>} Promise to return a token. Rejected if retrieving the token fails.
*/
private getToken(userId: string, tenantId: string, resourceId: string): Thenable<adal.TokenResponse> {
let self = this;
return new Promise<adal.TokenResponse>((resolve, reject) => {
let authorityUrl = url.resolve(self._metadata.settings.host, tenantId);
let context = new adal.AuthenticationContext(authorityUrl, null, self._tokenCache);
context.acquireToken(resourceId, userId, self._metadata.settings.clientId,
(error: Error, response: adal.TokenResponse | adal.ErrorResponse) => {
if (error) {
reject(error);
} else {
resolve(<adal.TokenResponse>response);
}
}
);
});
}
/**
* Performs a web request using the provided bearer token
* @param {TokenResponse} accessToken Bearer token for accessing the provided URI
* @param {string} uri URI to access
* @returns {Thenable<any>} Promise to return the deserialized body of the request. Rejected if error occurred.
*/
private makeWebRequest(accessToken: adal.TokenResponse, uri: string): Thenable<any> {
return new Promise<any>((resolve, reject) => {
// Setup parameters for the request
// NOTE: setting json true means the returned object will be deserialized
let params = {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken.accessToken}`
},
json: true
};
// Setup the callback to resolve/reject this promise
let callback = (error, response, body: { error: any; value: any; }) => {
if (error || body.error) {
reject(error || JSON.stringify(body.error));
} else {
resolve(body.value);
}
};
// Make the request
request.get(uri, params, callback);
});
}
private signIn(isAddAccount: boolean): Thenable<AzureAccount> {
let self = this;
// 1) Get the user code for this login
// 2) Get an access token from the device code
// 3) Get the list of tenants
// 4) Generate the AzureAccount object and return it
let tokenResponse: adal.TokenResponse = null;
return this.getDeviceLoginUserCode()
.then((result: InProgressAutoOAuth) => {
self._autoOAuthCancelled = false;
self._inProgressAutoOAuth = result;
return self.getDeviceLoginToken(self._inProgressAutoOAuth, isAddAccount);
})
.then((response: adal.TokenResponse) => {
tokenResponse = response;
self._autoOAuthCancelled = false;
self._inProgressAutoOAuth = null;
return self.getTenants(tokenResponse.userId, tokenResponse.userId);
})
.then((tenants: Tenant[]) => {
// Figure out where we're getting the identity from
let identityProvider = tokenResponse.identityProvider;
if (identityProvider) {
identityProvider = identityProvider.toLowerCase();
}
// Determine if this is a microsoft account
let 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);
// Calculate the display name for the user
let displayName = (tokenResponse.givenName && tokenResponse.familyName)
? `${tokenResponse.givenName} ${tokenResponse.familyName}`
: tokenResponse.userId;
// Calculate the home tenant display name to use for the contextual display name
let contextualDisplayName = msa
? localize('microsoftAccountDisplayName', 'Microsoft Account')
: tenants[0].displayName;
// Calculate the account type
let accountType = msa
? AzureAccountProvider.MicrosoftAccountType
: AzureAccountProvider.WorkSchoolAccountType;
return <AzureAccount>{
key: {
providerId: self._metadata.id,
accountId: tokenResponse.userId
},
name: tokenResponse.userId,
displayInfo: {
accountType: accountType,
contextualDisplayName: contextualDisplayName,
displayName: displayName
},
properties: {
isMsAccount: msa,
tenants: tenants
},
isStale: false
};
});
}
}
interface InProgressAutoOAuth {
context: adal.AuthenticationContext;
userCodeInfo: adal.UserCodeInfo;
}
@@ -0,0 +1,163 @@
/*---------------------------------------------------------------------------------------------
* 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 events from 'events';
import * as path from 'path';
import * as vscode from 'vscode';
import CredentialServiceTokenCache from './tokenCache';
import providerSettings from './providerSettings';
import { AzureAccountProvider } from './azureAccountProvider';
import { AzureAccountProviderMetadata, ProviderSettings } from './interfaces';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
export class AzureAccountProviderService implements vscode.Disposable {
// CONSTANTS ///////////////////////////////////////////////////////////////
private static CommandClearTokenCache = 'extension.clearTokenCache';
private static ConfigurationSection = 'accounts.azure';
private static CredentialNamespace = 'azureAccountProviderCredentials';
// MEMBER VARIABLES ////////////////////////////////////////////////////////
private _accountDisposals: { [accountProviderId: string]: vscode.Disposable };
private _accountProviders: { [accountProviderId: string]: AzureAccountProvider };
private _credentialProvider: data.CredentialProvider;
private _configChangePromiseChain: Thenable<void>;
private _currentConfig: vscode.WorkspaceConfiguration;
private _event: events.EventEmitter;
constructor(private _context: vscode.ExtensionContext, private _userStoragePath: string) {
this._accountDisposals = {};
this._accountProviders = {};
this._configChangePromiseChain = Promise.resolve();
this._currentConfig = null;
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(); });
// 1) Get a credential provider
// 2a) Store the credential provider for use later
// 2b) Register the configuration change handler
// 2c) Perform an initial config change handling
return data.credentials.getProvider(AzureAccountProviderService.CredentialNamespace)
.then(credProvider => {
self._credentialProvider = credProvider;
self._context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(self.onDidChangeConfiguration, self));
self.onDidChangeConfiguration();
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(`mssql: ${message}`);
},
err => {
let message = localize('clearTokenCacheFailure', 'Failed to clear token cache');
vscode.window.showErrorMessage(`mssql: ${message}: ${err}`);
});
}
private onDidChangeConfiguration(): void {
let self = this;
// Add a new change processing onto the existing promise change
this._configChangePromiseChain = this._configChangePromiseChain.then(() => {
// Grab the stored config and the latest config
let newConfig = vscode.workspace.getConfiguration(AzureAccountProviderService.ConfigurationSection);
let oldConfig = self._currentConfig;
self._currentConfig = newConfig;
// Determine what providers need to be changed
let providerChanges: Thenable<void>[] = [];
for(let provider of providerSettings) {
// If the old config doesn't exist, then assume everything was disabled
// There will always be a new config value
let oldConfigValue = oldConfig
? oldConfig.get<boolean>(provider.configKey)
: false;
let newConfigValue = newConfig.get<boolean>(provider.configKey);
// Case 1: Provider config has not changed - do nothing
if (oldConfigValue === newConfigValue) {
continue;
}
// Case 2: Provider was enabled and is now disabled - unregister provider
if (oldConfigValue && !newConfigValue) {
providerChanges.push(self.unregisterAccountProvider(provider));
}
// Case 3: Provider was disabled and is now enabled - register provider
if (!oldConfigValue && newConfigValue) {
providerChanges.push(self.registerAccountProvider(provider));
}
}
// Process all the changes before continuing
return Promise.all(providerChanges);
}).then(null, () => { return Promise.resolve(); });
}
private registerAccountProvider(provider: ProviderSettings): Thenable<void> {
let self = this;
return new Promise((resolve, reject) => {
try {
let tokenCacheKey = `azureTokenCache-${provider.metadata.id}`;
let tokenCachePath = path.join(this._userStoragePath, tokenCacheKey);
let tokenCache = new CredentialServiceTokenCache(self._credentialProvider, tokenCacheKey, tokenCachePath);
let accountProvider = new AzureAccountProvider(<AzureAccountProviderMetadata>provider.metadata, tokenCache);
self._accountProviders[provider.metadata.id] = accountProvider;
self._accountDisposals[provider.metadata.id] = data.accounts.registerAccountProvider(provider.metadata, accountProvider);
resolve();
} catch(e) {
console.error(`Failed to register account provider: ${e}`);
reject(e);
}
});
}
private unregisterAccountProvider(provider: ProviderSettings): Thenable<void> {
let self = this;
return new Promise((resolve, reject) => {
try {
self._accountDisposals[provider.metadata.id].dispose();
delete self._accountProviders[provider.metadata.id];
delete self._accountDisposals[provider.metadata.id];
resolve();
} catch(e) {
console.error(`Failed to unregister account provider: ${e}`);
reject(e);
}
});
}
}
@@ -0,0 +1,182 @@
'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;
}
/**
* Mapping of configuration key with the metadata to instantiate the account provider
*/
export interface ProviderSettings {
/**
* Key for configuration regarding whether the account provider is enabled
*/
configKey: string;
/**
* Metadata for the provider
*/
metadata: AzureAccountProviderMetadata;
}
/**
* Extension of account provider metadata to override settings type for Azure account providers
*/
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 | string;
/**
* 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 12 12"><defs><style>.cls-1{fill:#fff;}</style></defs><title>microsoft_logo-white</title><rect class="cls-1" x="0.06" y="0.06" width="5.65" height="5.65"/><rect class="cls-1" x="6.29" y="0.06" width="5.65" height="5.65"/><rect class="cls-1" x="0.06" y="6.3" width="5.65" height="5.65"/><rect class="cls-1" x="6.29" y="6.3" width="5.65" height="5.65"/></svg>

After

Width:  |  Height:  |  Size: 442 B

@@ -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:#231f20;}</style></defs><title>microsoft_account_Blk</title><rect class="cls-1" width="7.6" height="7.6"/><rect class="cls-1" x="8.4" width="7.6" height="7.6"/><rect class="cls-1" y="8.4" width="7.6" height="7.6"/><rect class="cls-1" x="8.4" y="8.4" width="7.6" height="7.6"/></svg>

After

Width:  |  Height:  |  Size: 400 B

@@ -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 @@
<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,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 nls from 'vscode-nls';
import { ProviderSettings } from './interfaces';
const localize = nls.loadMessageBundle();
const publicAzureSettings: ProviderSettings = {
configKey: 'enablePublicCloud',
metadata: {
displayName: localize('publicCloudDisplayName', 'Azure'),
id: 'azurePublicCloud',
settings: {
host: 'https://login.microsoftonline.com/',
clientId: 'a69788c6-1d43-44ed-9ca3-b83e194da255',
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: ProviderSettings = {
configKey: 'enableUsGovCloud',
metadata: {
displayName: localize('usGovCloudDisplayName', 'Azure (US Government)'),
id: 'usGovAzureCloud',
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: ProviderSettings = {
configKey: 'enableChinaCloud',
metadata: {
displayName: localize('chinaCloudDisplayName', 'Azure (China)'),
id: 'chinaAzureCloud',
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: ProviderSettings = {
configKey: 'enableGermanyCloud',
metadata: {
displayName: localize('germanyCloud', 'Azure (Germany)'),
id: 'germanyAzureCloud',
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'
}
}
};
// TODO: Enable China, Germany, and US Gov clouds: (#3031)
export default <ProviderSettings[]>[publicAzureSettings, /*chinaAzureSettings, germanyAzureSettings, usGovAzureSettings*/];
@@ -0,0 +1,291 @@
/*---------------------------------------------------------------------------------------------
* 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.TokenResponse[], callback: (error: Error, result: boolean) => void): void {
let self = this;
this.doOperation(() => {
return self.readCache()
.then(cache => self.addToCache(cache, entries))
.then(updatedCache => self.writeCache(updatedCache))
.then(
() => callback(null, false),
(err) => callback(err, true)
);
});
}
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: any, callback: (error: Error, results: any[]) => 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)
);
});
}
/**
* Wrapper to make callback-based find method into a thenable method
* @param query Partial object to use to look up tokens. Ideally should be partial of adal.TokenResponse
* @returns {Thenable<any[]>} Promise to return the matching adal.TokenResponse objects.
* Rejected if an error was sent in the callback
*/
public findThenable(query: any): Thenable<any[]> {
let self = this;
return new Promise<any[]>((resolve, reject) => {
self.find(query, (error: Error, results: any[]) => {
if (error) {
reject(error);
} else {
resolve(results);
}
});
});
}
public remove(entries: adal.TokenResponse[], callback: (error: Error, result: null) => void): void {
let self = this;
this.doOperation(() => {
return this.readCache()
.then(cache => self.removeFromCache(cache, entries))
.then(updatedCache => self.writeCache(updatedCache))
.then(
() => callback(null, null),
(err) => callback(err, null)
);
});
}
/**
* Wrapper to make callback-based remove method into a thenable method
* @param {TokenResponse[]} entries Array of entries to remove from the token cache
* @returns {Thenable<void>} Promise to remove the given tokens from the token cache
* Rejected if an error was sent in the callback
*/
public removeThenable(entries: adal.TokenResponse[]): Thenable<void> {
let self = this;
return new Promise<void>((resolve, reject) => {
self.remove(entries, (error: Error, result: null) => {
if (error) {
reject(error);
} else {
resolve();
}
});
});
}
// PRIVATE METHODS /////////////////////////////////////////////////////
private static findByKeyHelper(entry1: adal.TokenResponse, entry2: adal.TokenResponse): boolean {
return entry1._authority === entry2._authority
&& entry1._clientId === entry2._clientId
&& entry1.userId === entry2.userId
&& entry1.resource === entry2.resource;
}
private static findByPartial(entry: adal.TokenResponse, 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.TokenResponse[], entries: adal.TokenResponse[]): adal.TokenResponse[] {
// 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.TokenResponse) => {
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.TokenResponse[]> {
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');
// Deserialize the JSON into the array of tokens
let cacheObj = <adal.TokenResponse[]>JSON.parse(cacheJson);
for (let objIndex in cacheObj) {
// Rehydrate Date objects since they will always serialize as a string
cacheObj[objIndex].expiresOn = new Date(<string>cacheObj[objIndex].expiresOn);
}
return cacheObj;
} 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.TokenResponse[], entries: adal.TokenResponse[]): adal.TokenResponse[] {
entries.forEach((entry: adal.TokenResponse) => {
// 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.TokenResponse[]): 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;
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"service": {
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
"version": "1.2.0-alpha.37",
"version": "1.2.0-alpha.49",
"downloadFileNames": {
"Windows_86": "win-x86-netcoreapp2.0.zip",
"Windows_64": "win-x64-netcoreapp2.0.zip",
@@ -8,10 +8,15 @@ import vscode = require('vscode');
import data = require('data');
import { Constants } from '../models/constants';
import { Serialization } from '../serialize/serialization';
import { AzureResourceProvider } from '../resourceProvider/resourceProvider';
import { AzureResourceProvider } from '../resourceprovider/resourceprovider';
import { CredentialStore } from '../credentialstore/credentialstore';
import {IExtensionConstants, Telemetry, SharedConstants, SqlToolsServiceClient, VscodeWrapper, Utils, PlatformInformation} from 'extensions-modules';
import { LanguageClient } from 'dataprotocol-client';
import * as fs from 'fs';
import * as path from 'path';
import { AzureAccountProviderService } from '../account-provider/azureAccountProviderService';
/**
* The main controller class that initializes the extension
@@ -92,77 +97,94 @@ export default class MainController implements vscode.Disposable {
// initialize language service client
return new Promise<boolean>( (resolve, reject) => {
const self = this;
SqlToolsServiceClient.instance.initialize(self._context).then(serverResult => {
// Initialize telemetry
Telemetry.initialize(self._context, new Constants());
let constants = new Constants();
// telemetry for activation
Telemetry.sendTelemetryEvent('ExtensionActivated', {},
{ serviceInstalled: serverResult.installedBeforeInitializing ? 1 : 0 }
);
// 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;
}
/*
self.createSerializationClient().then(serializationClient => {
let serialization = new Serialization(self._client, serializationClient);
// Serialization
let serializationProvider: data.SerializationProvider = {
handle: 0,
saveAs(saveFormat: string, savePath: string, results: string, appendToFile: boolean): Thenable<data.SaveResultRequestResult> {
return self._serialization.saveAs(saveFormat, savePath, results, appendToFile);
}
};
data.serialization.registerProvider(serializationProvider);
}, error => {
Utils.logDebug('Cannot find Serialization executables. error: ' + error , MainController._extensionConstants.extensionConfigSectionName);
});
// Create the provider service and activate
const accountProviderService = new AzureAccountProviderService(this._context, storagePath);
this._context.subscriptions.push(accountProviderService);
accountProviderService.activate();
self.createResourceProviderClient().then(rpClient => {
let resourceProvider = new AzureResourceProvider(self._client, rpClient);
data.resources.registerResourceProvider({
displayName: 'Azure SQL Resource Provider', // TODO Localize
id: 'Microsoft.Azure.SQL.ResourceProvider',
settings: {
const self = this;
SqlToolsServiceClient.instance.initialize(self._context).then(serverResult => {
}
}, resourceProvider);
Utils.logDebug('resourceProvider registered', MainController._extensionConstants.extensionConfigSectionName);
}, error => {
Utils.logDebug('Cannot find ResourceProvider executables. error: ' + error , MainController._extensionConstants.extensionConfigSectionName);
});
*/
// Initialize telemetry
Telemetry.initialize(self._context, new Constants());
self.createCredentialClient().then(credentialClient => {
// telemetry for activation
Telemetry.sendTelemetryEvent('ExtensionActivated', {},
{ serviceInstalled: serverResult.installedBeforeInitializing ? 1 : 0 }
);
self._credentialStore.languageClient = credentialClient;
let credentialProvider: data.CredentialProvider = {
handle: 0,
saveCredential(credentialId: string, password: string): Thenable<boolean> {
return self._credentialStore.saveCredential(credentialId, password);
},
readCredential(credentialId: string): Thenable<data.Credential> {
return self._credentialStore.readCredential(credentialId);
},
deleteCredential(credentialId: string): Thenable<boolean> {
return self._credentialStore.deleteCredential(credentialId);
}
};
data.credentials.registerProvider(credentialProvider);
Utils.logDebug('credentialProvider registered', MainController._extensionConstants.extensionConfigSectionName);
}, error => {
Utils.logDebug('Cannot find credentials executables. error: ' + error , MainController._extensionConstants.extensionConfigSectionName);
});
Utils.logDebug(SharedConstants.extensionActivated, MainController._extensionConstants.extensionConfigSectionName);
self._initialized = true;
resolve(true);
}).catch(err => {
Telemetry.sendTelemetryEventForException(err, 'initialize', MainController._extensionConstants.extensionConfigSectionName);
reject(err);
self.createSerializationClient().then(serializationClient => {
let serialization = new Serialization(self._client, serializationClient);
// Serialization
let serializationProvider: data.SerializationProvider = {
handle: 0,
saveAs(saveFormat: string, savePath: string, results: string, appendToFile: boolean): Thenable<data.SaveResultRequestResult> {
return self._serialization.saveAs(saveFormat, savePath, results, appendToFile);
}
};
data.serialization.registerProvider(serializationProvider);
}, error => {
Utils.logDebug('Cannot find Serialization executables. error: ' + error , MainController._extensionConstants.extensionConfigSectionName);
});
self.createResourceProviderClient().then(rpClient => {
let resourceProvider = new AzureResourceProvider(self._client, rpClient);
data.resources.registerResourceProvider({
displayName: 'Azure SQL Resource Provider', // TODO Localize
id: 'Microsoft.Azure.SQL.ResourceProvider',
settings: {
}
}, resourceProvider);
Utils.logDebug('resourceProvider registered', MainController._extensionConstants.extensionConfigSectionName);
}, error => {
Utils.logDebug('Cannot find ResourceProvider executables. error: ' + error , MainController._extensionConstants.extensionConfigSectionName);
});
self.createCredentialClient().then(credentialClient => {
self._credentialStore.languageClient = credentialClient;
let credentialProvider: data.CredentialProvider = {
handle: 0,
saveCredential(credentialId: string, password: string): Thenable<boolean> {
return self._credentialStore.saveCredential(credentialId, password);
},
readCredential(credentialId: string): Thenable<data.Credential> {
return self._credentialStore.readCredential(credentialId);
},
deleteCredential(credentialId: string): Thenable<boolean> {
return self._credentialStore.deleteCredential(credentialId);
}
};
data.credentials.registerProvider(credentialProvider);
Utils.logDebug('credentialProvider registered', MainController._extensionConstants.extensionConfigSectionName);
}, error => {
Utils.logDebug('Cannot find credentials executables. error: ' + error , MainController._extensionConstants.extensionConfigSectionName);
});
Utils.logDebug(SharedConstants.extensionActivated, MainController._extensionConstants.extensionConfigSectionName);
self._initialized = true;
resolve(true);
}).catch(err => {
Telemetry.sendTelemetryEventForException(err, 'initialize', MainController._extensionConstants.extensionConfigSectionName);
reject(err);
});
});
}
}
+1 -2
View File
@@ -4,5 +4,4 @@
*--------------------------------------------------------------------------------------------*/
/// <reference path='../../../../../src/vs/vscode.d.ts'/>
/// <reference path='../../../../../src/sql/data.d.ts'/>
/// <reference types='@types/node'/>
/// <reference path='../../../../../src/sql/data.d.ts'/>
+1918 -2
View File
File diff suppressed because it is too large Load Diff
+17
View File
@@ -0,0 +1,17 @@
{
"accounts.azure.enableUsGovCloud": {
"type": "boolean",
"default": false,
"description": "%config.enableUsGovCloudDescription%"
},
"accounts.azure.enableChinaCloud": {
"type": "boolean",
"default": false,
"description": "%config.enableChinaCloudDescription%"
},
"accounts.azure.enableGermanyCloud": {
"type": "boolean",
"default": false,
"description": "%config.enableGermanyCloudDescription%"
}
}
+133 -2
View File
@@ -46,10 +46,38 @@
"path": "./snippets/mssql.json"
}
],
"commands": [
{
"command": "extension.clearTokenCache",
"title": "%extension.clearTokenCache%",
"category": "Azure Accounts"
}
],
"account-type": [
{
"id": "microsoft",
"icon": {
"light": "./out/account-provider/media/microsoft_account_light.svg",
"dark": "./out/account-provider/media/microsoft_account_dark.svg"
}
},
{
"id": "work_school",
"icon": {
"light": "./out/account-provider/media/work_school_account_light.svg",
"dark": "./out/account-provider/media/work_school_account_dark.svg"
}
}
],
"configuration": {
"type": "object",
"title": "MSSQL configuration",
"properties": {
"accounts.azure.enablePublicCloud": {
"type": "boolean",
"default": true,
"description": "%config.enablePublicCloudDescription%"
},
"mssql.query.displayBitAsNumber": {
"type": "boolean",
"default": true,
@@ -101,13 +129,116 @@
"default": false
}
}
},
"dashboard": {
"provider": "MSSQL",
"flavors": [
{
"flavor": "on_prem",
"condition": {
"field": "isCloud",
"operator": "!=",
"value": true
},
"databaseProperties": [
{
"displayName": "Recovery Model",
"value": "recoveryModel"
},
{
"displayName": "Last Database Backup",
"value": "lastBackupDate",
"ignore": [
"1/1/0001 12:00:00 AM"
]
},
{
"displayName": "Last Log Backup",
"value": "lastLogBackupDate",
"ignore": [
"1/1/0001 12:00:00 AM"
]
},
{
"displayName": "Compatibility Level",
"value": "compatibilityLevel"
},
{
"displayName": "Owner",
"value": "owner"
}
],
"serverProperties": [
{
"displayName": "Version",
"value": "serverVersion"
},
{
"displayName": "Edition",
"value": "serverEdition"
},
{
"displayName": "Computer Name",
"value": "machineName"
},
{
"displayName": "OS Version",
"value": "osVersion"
}
]
},
{
"flavor": "cloud",
"condition": {
"field": "isCloud",
"operator": "==",
"value": true
},
"databaseProperties": [
{
"displayName": "Edition",
"value": "azureEdition"
},
{
"displayName": "Pricing Tier",
"value": "serviceLevelObjective"
},
{
"displayName": "Compatibility Level",
"value": "compatibilityLevel"
},
{
"displayName": "Owner",
"value": "owner"
}
],
"serverProperties": [
{
"displayName": "Version",
"value": "serverVersion"
},
{
"displayName": "Type",
"value": "serverEdition"
}
]
}
]
}
},
"dependencies": {
"dataprotocol-client": "file:../../dataprotocol-node/client",
"extensions-modules": "file:../../extensions-modules"
"extensions-modules": "file:../../extensions-modules",
"adal-node": "0.1.25",
"decompress": "^4.0.0",
"fs-extra-promise": "^1.0.1",
"opener": "1.4.3",
"request": "2.63.0",
"vscode-extension-telemetry": "^0.0.8",
"vscode-nls": "2.0.2"
},
"devDependencies": {
"vscode": "1.0.1"
"vscode": "1.0.1",
"@types/node": "^8.0.24"
}
}
+5 -6
View File
@@ -1,8 +1,7 @@
{
"json.schemas.desc": "Associate schemas to JSON files in the current project",
"json.schemas.url.desc": "A URL to a schema or a relative path to a schema in the current directory",
"json.schemas.fileMatch.desc": "An array of file patterns to match against when resolving JSON files to schemas.",
"json.schemas.fileMatch.item.desc": "A file pattern that can contain '*' to match against when resolving JSON files to schemas.",
"json.schemas.schema.desc": "The schema definition for the given URL. The schema only needs to be provided to avoid accesses to the schema URL.",
"json.format.enable.desc": "Enable/disable default JSON formatter (requires restart)"
"extension.clearTokenCache": "Clear Azure Account Token Cache",
"config.enablePublicCloudDescription": "Should Azure public cloud integration be enabled",
"config.enableUsGovCloudDescription": "Should US Government Azure cloud (Fairfax) integration be enabled",
"config.enableChinaCloudDescription": "Should Azure China integration be enabled",
"config.enableGermanyCloudDescription": "Should Azure Germany integration be enabled"
}
+3 -3
View File
@@ -3,9 +3,9 @@
"version": "0.0.1",
"dependencies": {
"typescript": {
"version": "2.5.2",
"from": "typescript@2.5.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.2.tgz"
"version": "2.6.1",
"from": "typescript@2.6.1",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.6.1.tgz"
}
}
}
+1 -1
View File
@@ -3,7 +3,7 @@
"version": "0.0.1",
"description": "Dependencies shared by all extensions",
"dependencies": {
"typescript": "2.5.2"
"typescript": "2.6.1"
},
"scripts": {
"postinstall": "node ./postinstall"
@@ -22,5 +22,11 @@
["(", ")"],
["\"", "\""],
["'", "'"]
]
],
"folding": {
"markers": {
"start": "^\\s*#region\\b",
"end": "^\\s*#endregion\\b"
}
}
}
+4
View File
@@ -14,6 +14,10 @@
"language": "powershell",
"scopeName": "source.powershell",
"path": "./syntaxes/PowershellSyntax.tmLanguage"
}],
"snippets": [{
"language": "powershell",
"path": "./snippets/powershell.json"
}]
},
"scripts": {
@@ -0,0 +1,16 @@
{
"Region Start": {
"prefix": "#region",
"body": [
"#region $0"
],
"description": "Folding Region Start"
},
"Region End": {
"prefix": "#endregion",
"body": [
"#endregion"
],
"description": "Folding Region End"
}
}
@@ -45,7 +45,7 @@
</dict>
<dict>
<key>begin</key>
<string>(?&lt;![\\-])#</string>
<string>(?&lt;![`\\-])#</string>
<key>end</key>
<string>$</string>
<key>name</key>
@@ -246,7 +246,7 @@
</dict>
<dict>
<key>match</key>
<string>(?&lt;!\w)((?i:begin|break|catch|continue|data|define|do|dynamicparam|else|elseif|end|exit|finally|for|foreach(?!-object)|from|if|in|inlinescript|parallel|param|process|return|switch|throw|trap|try|until|using|var|where(?!=-object)|while)|%|\?)(?!\w)</string>
<string>(?&lt;!\w)((?i:begin|break|catch|continue|data|define|do|dynamicparam|else|elseif|end|exit|finally|for|foreach(?!-object)|from|if|in|inlinescript|parallel|param|process|return|switch|throw|trap|try|until|using|var|where(?!-object)|while)|%|\?)(?!\w)</string>
<key>name</key>
<string>keyword.control.powershell</string>
</dict>
@@ -426,6 +426,14 @@
<key>name</key>
<string>support.function.powershell</string>
</dict>
<dict>
<key>comment</key>
<string>Builtin cmdlets with reserved verbs</string>
<key>match</key>
<string>(?&lt;!\w)(?i:where-object)(?!\w)</string>
<key>name</key>
<string>support.function.powershell</string>
</dict>
</array>
</dict>
<key>commentEmbeddedDocs</key>
+3 -3
View File
@@ -7,12 +7,12 @@
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceRoot}"
"--extensionDevelopmentPath=${workspaceFolder}"
],
"stopOnEntry": false,
"sourceMaps": true,
"outDir": "${workspaceRoot}/out",
"outDir": "${workspaceFolder}/out",
"preLaunchTask": "npm"
}
}
]
}
+1 -1
View File
@@ -1,5 +1,5 @@
// Available variables which can be used inside of strings.
// ${workspaceRoot}: the root folder of the team
// ${workspaceFolder}: the root folder of the team
// ${file}: the current opened file
// ${fileBasename}: the current opened file's basename
// ${fileDirname}: the current opened file's dirname
@@ -21,6 +21,12 @@
["(", ")"],
["\"", "\""],
["'", "'"]
]
// enhancedBrackets: [ { open: /.*:\s*$/, closeComplete: 'else:' } ],
}
],
"folding": {
"offSide": true,
"markers": {
"start": "^\\s*#region\\b",
"end": "^\\s*#endregion\\b"
}
}
}
@@ -5294,8 +5294,8 @@
"c": "(",
"t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python support.other.parenthesis.regexp punctuation.parenthesis.begin.regexp",
"r": {
"dark_plus": "string.regexp: #D16969",
"light_plus": "string.regexp: #811F3F",
"dark_plus": "support.other.parenthesis.regexp: #CE9178",
"light_plus": "support.other.parenthesis.regexp: #D16969",
"dark_vs": "string.regexp: #D16969",
"light_vs": "string.regexp: #811F3F",
"hc_black": "string.regexp: #D16969"
@@ -5305,8 +5305,8 @@
"c": "[",
"t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python meta.character.set.regexp punctuation.character.set.begin.regexp constant.other.set.regexp",
"r": {
"dark_plus": "string.regexp: #D16969",
"light_plus": "string.regexp: #811F3F",
"dark_plus": "punctuation.character.set.begin.regexp: #CE9178",
"light_plus": "punctuation.character.set.begin.regexp: #D16969",
"dark_vs": "string.regexp: #D16969",
"light_vs": "string.regexp: #811F3F",
"hc_black": "string.regexp: #D16969"
@@ -5316,19 +5316,19 @@
"c": "0-9-",
"t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python meta.character.set.regexp constant.character.set.regexp",
"r": {
"dark_plus": "string.regexp: #D16969",
"light_plus": "string.regexp: #811F3F",
"dark_plus": "constant.character.set.regexp: #D16969",
"light_plus": "constant.character.set.regexp: #811F3F",
"dark_vs": "string.regexp: #D16969",
"light_vs": "string.regexp: #811F3F",
"hc_black": "string.regexp: #D16969"
"hc_black": "constant.character: #569CD6"
}
},
{
"c": "]",
"t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python meta.character.set.regexp punctuation.character.set.end.regexp constant.other.set.regexp",
"r": {
"dark_plus": "string.regexp: #D16969",
"light_plus": "string.regexp: #811F3F",
"dark_plus": "punctuation.character.set.end.regexp: #CE9178",
"light_plus": "punctuation.character.set.end.regexp: #D16969",
"dark_vs": "string.regexp: #D16969",
"light_vs": "string.regexp: #811F3F",
"hc_black": "string.regexp: #D16969"
@@ -5338,8 +5338,8 @@
"c": "*",
"t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python keyword.operator.quantifier.regexp",
"r": {
"dark_plus": "keyword.operator: #D4D4D4",
"light_plus": "keyword.operator: #000000",
"dark_plus": "keyword.operator.quantifier.regexp: #D7BA7D",
"light_plus": "keyword.operator.quantifier.regexp: #000000",
"dark_vs": "keyword.operator: #D4D4D4",
"light_vs": "keyword.operator: #000000",
"hc_black": "keyword.operator: #D4D4D4"
@@ -5349,8 +5349,8 @@
"c": ")",
"t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python support.other.parenthesis.regexp punctuation.parenthesis.end.regexp",
"r": {
"dark_plus": "string.regexp: #D16969",
"light_plus": "string.regexp: #811F3F",
"dark_plus": "support.other.parenthesis.regexp: #CE9178",
"light_plus": "support.other.parenthesis.regexp: #D16969",
"dark_vs": "string.regexp: #D16969",
"light_vs": "string.regexp: #811F3F",
"hc_black": "string.regexp: #D16969"
@@ -5371,8 +5371,8 @@
"c": "*",
"t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python keyword.operator.quantifier.regexp",
"r": {
"dark_plus": "keyword.operator: #D4D4D4",
"light_plus": "keyword.operator: #000000",
"dark_plus": "keyword.operator.quantifier.regexp: #D7BA7D",
"light_plus": "keyword.operator.quantifier.regexp: #000000",
"dark_vs": "keyword.operator: #D4D4D4",
"light_vs": "keyword.operator: #000000",
"hc_black": "keyword.operator: #D4D4D4"
@@ -5382,8 +5382,8 @@
"c": "(",
"t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python support.other.parenthesis.regexp punctuation.parenthesis.begin.regexp",
"r": {
"dark_plus": "string.regexp: #D16969",
"light_plus": "string.regexp: #811F3F",
"dark_plus": "support.other.parenthesis.regexp: #CE9178",
"light_plus": "support.other.parenthesis.regexp: #D16969",
"dark_vs": "string.regexp: #D16969",
"light_vs": "string.regexp: #811F3F",
"hc_black": "string.regexp: #D16969"
@@ -5393,8 +5393,8 @@
"c": "[",
"t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python meta.character.set.regexp punctuation.character.set.begin.regexp constant.other.set.regexp",
"r": {
"dark_plus": "string.regexp: #D16969",
"light_plus": "string.regexp: #811F3F",
"dark_plus": "punctuation.character.set.begin.regexp: #CE9178",
"light_plus": "punctuation.character.set.begin.regexp: #D16969",
"dark_vs": "string.regexp: #D16969",
"light_vs": "string.regexp: #811F3F",
"hc_black": "string.regexp: #D16969"
@@ -5404,19 +5404,19 @@
"c": "A-Za-z",
"t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python meta.character.set.regexp constant.character.set.regexp",
"r": {
"dark_plus": "string.regexp: #D16969",
"light_plus": "string.regexp: #811F3F",
"dark_plus": "constant.character.set.regexp: #D16969",
"light_plus": "constant.character.set.regexp: #811F3F",
"dark_vs": "string.regexp: #D16969",
"light_vs": "string.regexp: #811F3F",
"hc_black": "string.regexp: #D16969"
"hc_black": "constant.character: #569CD6"
}
},
{
"c": "]",
"t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python meta.character.set.regexp punctuation.character.set.end.regexp constant.other.set.regexp",
"r": {
"dark_plus": "string.regexp: #D16969",
"light_plus": "string.regexp: #811F3F",
"dark_plus": "punctuation.character.set.end.regexp: #CE9178",
"light_plus": "punctuation.character.set.end.regexp: #D16969",
"dark_vs": "string.regexp: #D16969",
"light_vs": "string.regexp: #811F3F",
"hc_black": "string.regexp: #D16969"
@@ -5426,8 +5426,8 @@
"c": "+",
"t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python keyword.operator.quantifier.regexp",
"r": {
"dark_plus": "keyword.operator: #D4D4D4",
"light_plus": "keyword.operator: #000000",
"dark_plus": "keyword.operator.quantifier.regexp: #D7BA7D",
"light_plus": "keyword.operator.quantifier.regexp: #000000",
"dark_vs": "keyword.operator: #D4D4D4",
"light_vs": "keyword.operator: #000000",
"hc_black": "keyword.operator: #D4D4D4"
@@ -5437,8 +5437,8 @@
"c": ")",
"t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python support.other.parenthesis.regexp punctuation.parenthesis.end.regexp",
"r": {
"dark_plus": "string.regexp: #D16969",
"light_plus": "string.regexp: #811F3F",
"dark_plus": "support.other.parenthesis.regexp: #CE9178",
"light_plus": "support.other.parenthesis.regexp: #D16969",
"dark_vs": "string.regexp: #D16969",
"light_vs": "string.regexp: #811F3F",
"hc_black": "string.regexp: #D16969"
@@ -5470,8 +5470,8 @@
"c": "+",
"t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python keyword.operator.quantifier.regexp",
"r": {
"dark_plus": "keyword.operator: #D4D4D4",
"light_plus": "keyword.operator: #000000",
"dark_plus": "keyword.operator.quantifier.regexp: #D7BA7D",
"light_plus": "keyword.operator.quantifier.regexp: #000000",
"dark_vs": "keyword.operator: #D4D4D4",
"light_vs": "keyword.operator: #000000",
"hc_black": "keyword.operator: #D4D4D4"
@@ -5481,8 +5481,8 @@
"c": "(",
"t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python support.other.parenthesis.regexp punctuation.parenthesis.begin.regexp",
"r": {
"dark_plus": "string.regexp: #D16969",
"light_plus": "string.regexp: #811F3F",
"dark_plus": "support.other.parenthesis.regexp: #CE9178",
"light_plus": "support.other.parenthesis.regexp: #D16969",
"dark_vs": "string.regexp: #D16969",
"light_vs": "string.regexp: #811F3F",
"hc_black": "string.regexp: #D16969"
@@ -5503,8 +5503,8 @@
"c": "*",
"t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python keyword.operator.quantifier.regexp",
"r": {
"dark_plus": "keyword.operator: #D4D4D4",
"light_plus": "keyword.operator: #000000",
"dark_plus": "keyword.operator.quantifier.regexp: #D7BA7D",
"light_plus": "keyword.operator.quantifier.regexp: #000000",
"dark_vs": "keyword.operator: #D4D4D4",
"light_vs": "keyword.operator: #000000",
"hc_black": "keyword.operator: #D4D4D4"
@@ -5514,8 +5514,8 @@
"c": ")",
"t": "source.python meta.function-call.python meta.function-call.arguments.python string.regexp.quoted.single.python support.other.parenthesis.regexp punctuation.parenthesis.end.regexp",
"r": {
"dark_plus": "string.regexp: #D16969",
"light_plus": "string.regexp: #811F3F",
"dark_plus": "support.other.parenthesis.regexp: #CE9178",
"light_plus": "support.other.parenthesis.regexp: #D16969",
"dark_vs": "string.regexp: #D16969",
"light_vs": "string.regexp: #811F3F",
"hc_black": "string.regexp: #D16969"
@@ -6504,8 +6504,8 @@
"c": "[",
"t": "source.python string.regexp.quoted.multi.python meta.character.set.regexp punctuation.character.set.begin.regexp constant.other.set.regexp",
"r": {
"dark_plus": "string.regexp: #D16969",
"light_plus": "string.regexp: #811F3F",
"dark_plus": "punctuation.character.set.begin.regexp: #CE9178",
"light_plus": "punctuation.character.set.begin.regexp: #D16969",
"dark_vs": "string.regexp: #D16969",
"light_vs": "string.regexp: #811F3F",
"hc_black": "string.regexp: #D16969"
@@ -6515,11 +6515,11 @@
"c": "1,2)`` leads to",
"t": "source.python string.regexp.quoted.multi.python meta.character.set.regexp constant.character.set.regexp",
"r": {
"dark_plus": "string.regexp: #D16969",
"light_plus": "string.regexp: #811F3F",
"dark_plus": "constant.character.set.regexp: #D16969",
"light_plus": "constant.character.set.regexp: #811F3F",
"dark_vs": "string.regexp: #D16969",
"light_vs": "string.regexp: #811F3F",
"hc_black": "string.regexp: #D16969"
"hc_black": "constant.character: #569CD6"
}
},
{
+3 -13
View File
@@ -1,17 +1,7 @@
// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS:
[{
"name": "textmate/r.tmbundle",
"name": "Ikuyadeu/vscode-R",
"version": "0.0.0",
"license": "TextMate Bundle License",
"repositoryURL": "https://github.com/textmate/r.tmbundle",
"licenseDetail": [
"Copyright (c) textmate-r.tmbundle 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."
]
"license": "MIT",
"repositoryURL": "https://github.com/Ikuyadeu/vscode-R"
}]
+1 -1
View File
@@ -4,7 +4,7 @@
"publisher": "vscode",
"engines": { "vscode": "*" },
"scripts": {
"update-grammar": "node ../../build/npm/update-grammar.js textmate/r.tmbundle Syntaxes/R.plist ./syntaxes/r.tmLanguage.json"
"update-grammar": "node ../../build/npm/update-grammar.js Ikuyadeu/vscode-R syntax/r.json ./syntaxes/r.tmLanguage.json"
},
"contributes": {
"languages": [{
File diff suppressed because one or more lines are too long
+356 -136
View File
@@ -22,8 +22,8 @@
}
},
{
"c": "#",
"t": "source.r comment.line.number-sign.r punctuation.definition.comment.r",
"c": "#'",
"t": "source.r comment.line.roxygen.r punctuation.definition.comment.r",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
@@ -33,8 +33,8 @@
}
},
{
"c": "' Add together two numbers.",
"t": "source.r comment.line.number-sign.r",
"c": " Add together two numbers.",
"t": "source.r comment.line.roxygen.r",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
@@ -44,8 +44,8 @@
}
},
{
"c": "#",
"t": "source.r comment.line.number-sign.r punctuation.definition.comment.r",
"c": "#'",
"t": "source.r comment.line.roxygen.r punctuation.definition.comment.r",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
@@ -55,8 +55,8 @@
}
},
{
"c": "'",
"t": "source.r comment.line.number-sign.r",
"c": "#'",
"t": "source.r comment.line.roxygen.r punctuation.definition.comment.r",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
@@ -66,8 +66,8 @@
}
},
{
"c": "#",
"t": "source.r comment.line.number-sign.r punctuation.definition.comment.r",
"c": " ",
"t": "source.r comment.line.roxygen.r",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
@@ -77,8 +77,19 @@
}
},
{
"c": "' @param x A number.",
"t": "source.r comment.line.number-sign.r",
"c": "@param",
"t": "source.r comment.line.roxygen.r keyword.other.r",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
"dark_vs": "keyword: #569CD6",
"light_vs": "keyword: #0000FF",
"hc_black": "keyword: #569CD6"
}
},
{
"c": " ",
"t": "source.r comment.line.roxygen.r",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
@@ -88,8 +99,19 @@
}
},
{
"c": "#",
"t": "source.r comment.line.number-sign.r punctuation.definition.comment.r",
"c": "x",
"t": "source.r comment.line.roxygen.r variable.parameter.r",
"r": {
"dark_plus": "variable: #9CDCFE",
"light_plus": "variable: #001080",
"dark_vs": "comment: #608B4E",
"light_vs": "comment: #008000",
"hc_black": "variable: #9CDCFE"
}
},
{
"c": " A number.",
"t": "source.r comment.line.roxygen.r",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
@@ -99,8 +121,8 @@
}
},
{
"c": "' @param y A number.",
"t": "source.r comment.line.number-sign.r",
"c": "#'",
"t": "source.r comment.line.roxygen.r punctuation.definition.comment.r",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
@@ -110,8 +132,8 @@
}
},
{
"c": "#",
"t": "source.r comment.line.number-sign.r punctuation.definition.comment.r",
"c": " ",
"t": "source.r comment.line.roxygen.r",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
@@ -121,8 +143,19 @@
}
},
{
"c": "' @return The sum of \\code{x} and \\code{y}.",
"t": "source.r comment.line.number-sign.r",
"c": "@param",
"t": "source.r comment.line.roxygen.r keyword.other.r",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
"dark_vs": "keyword: #569CD6",
"light_vs": "keyword: #0000FF",
"hc_black": "keyword: #569CD6"
}
},
{
"c": " ",
"t": "source.r comment.line.roxygen.r",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
@@ -132,8 +165,19 @@
}
},
{
"c": "#",
"t": "source.r comment.line.number-sign.r punctuation.definition.comment.r",
"c": "y",
"t": "source.r comment.line.roxygen.r variable.parameter.r",
"r": {
"dark_plus": "variable: #9CDCFE",
"light_plus": "variable: #001080",
"dark_vs": "comment: #608B4E",
"light_vs": "comment: #008000",
"hc_black": "variable: #9CDCFE"
}
},
{
"c": " A number.",
"t": "source.r comment.line.roxygen.r",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
@@ -143,8 +187,8 @@
}
},
{
"c": "' @examples",
"t": "source.r comment.line.number-sign.r",
"c": "#'",
"t": "source.r comment.line.roxygen.r punctuation.definition.comment.r",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
@@ -154,8 +198,8 @@
}
},
{
"c": "#",
"t": "source.r comment.line.number-sign.r punctuation.definition.comment.r",
"c": " ",
"t": "source.r comment.line.roxygen.r",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
@@ -165,8 +209,19 @@
}
},
{
"c": "' add(1, 1)",
"t": "source.r comment.line.number-sign.r",
"c": "@return",
"t": "source.r comment.line.roxygen.r keyword.other.r",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
"dark_vs": "keyword: #569CD6",
"light_vs": "keyword: #0000FF",
"hc_black": "keyword: #569CD6"
}
},
{
"c": " The sum of \\code{x} and \\code{y}.",
"t": "source.r comment.line.roxygen.r",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
@@ -176,8 +231,8 @@
}
},
{
"c": "#",
"t": "source.r comment.line.number-sign.r punctuation.definition.comment.r",
"c": "#'",
"t": "source.r comment.line.roxygen.r punctuation.definition.comment.r",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
@@ -187,8 +242,63 @@
}
},
{
"c": "' add(10, 1)",
"t": "source.r comment.line.number-sign.r",
"c": " ",
"t": "source.r comment.line.roxygen.r",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
"dark_vs": "comment: #608B4E",
"light_vs": "comment: #008000",
"hc_black": "comment: #7CA668"
}
},
{
"c": "@examples",
"t": "source.r comment.line.roxygen.r keyword.other.r",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
"dark_vs": "keyword: #569CD6",
"light_vs": "keyword: #0000FF",
"hc_black": "keyword: #569CD6"
}
},
{
"c": "#'",
"t": "source.r comment.line.roxygen.r punctuation.definition.comment.r",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
"dark_vs": "comment: #608B4E",
"light_vs": "comment: #008000",
"hc_black": "comment: #7CA668"
}
},
{
"c": " add(1, 1)",
"t": "source.r comment.line.roxygen.r",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
"dark_vs": "comment: #608B4E",
"light_vs": "comment: #008000",
"hc_black": "comment: #7CA668"
}
},
{
"c": "#'",
"t": "source.r comment.line.roxygen.r punctuation.definition.comment.r",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
"dark_vs": "comment: #608B4E",
"light_vs": "comment: #008000",
"hc_black": "comment: #7CA668"
}
},
{
"c": " add(10, 1)",
"t": "source.r comment.line.roxygen.r",
"r": {
"dark_plus": "comment: #608B4E",
"light_plus": "comment: #008000",
@@ -243,7 +353,7 @@
},
{
"c": "function",
"t": "source.r meta.function.r keyword.control.r",
"t": "source.r meta.function.r meta.function.r keyword.control.r",
"r": {
"dark_plus": "keyword.control: #C586C0",
"light_plus": "keyword.control: #AF00DB",
@@ -254,7 +364,7 @@
},
{
"c": "(",
"t": "source.r",
"t": "source.r meta.function.r meta.function.r punctuation.section.parens.begin.r",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@@ -265,7 +375,7 @@
},
{
"c": "x",
"t": "source.r variable.other.r",
"t": "source.r meta.function.r meta.function.r meta.function.parameters.r variable.parameter.r",
"r": {
"dark_plus": "variable: #9CDCFE",
"light_plus": "variable: #001080",
@@ -275,8 +385,19 @@
}
},
{
"c": ", ",
"t": "source.r",
"c": ",",
"t": "source.r meta.function.r meta.function.r meta.function.parameters.r punctuation.separator.parameters.r",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": " ",
"t": "source.r meta.function.r meta.function.r meta.function.parameters.r",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@@ -287,7 +408,7 @@
},
{
"c": "y",
"t": "source.r variable.other.r",
"t": "source.r meta.function.r meta.function.r meta.function.parameters.r variable.parameter.r",
"r": {
"dark_plus": "variable: #9CDCFE",
"light_plus": "variable: #001080",
@@ -297,7 +418,18 @@
}
},
{
"c": ") ",
"c": ")",
"t": "source.r meta.function.r meta.function.r punctuation.section.parens.end.r",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": " ",
"t": "source.r",
"r": {
"dark_plus": "default: #D4D4D4",
@@ -309,7 +441,7 @@
},
{
"c": "{",
"t": "source.r meta.block.r punctuation.section.block.begin.r",
"t": "source.r punctuation.section.braces.begin.r",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@@ -320,7 +452,7 @@
},
{
"c": " ",
"t": "source.r meta.block.r",
"t": "source.r",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@@ -331,7 +463,7 @@
},
{
"c": "x",
"t": "source.r meta.block.r variable.other.r",
"t": "source.r variable.other.r",
"r": {
"dark_plus": "variable: #9CDCFE",
"light_plus": "variable: #001080",
@@ -342,7 +474,7 @@
},
{
"c": " ",
"t": "source.r meta.block.r",
"t": "source.r",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@@ -353,83 +485,6 @@
},
{
"c": "+",
"t": "source.r meta.block.r keyword.operator.arithmetic.r",
"r": {
"dark_plus": "keyword.operator: #D4D4D4",
"light_plus": "keyword.operator: #000000",
"dark_vs": "keyword.operator: #D4D4D4",
"light_vs": "keyword.operator: #000000",
"hc_black": "keyword.operator: #D4D4D4"
}
},
{
"c": " ",
"t": "source.r meta.block.r",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "y",
"t": "source.r meta.block.r variable.other.r",
"r": {
"dark_plus": "variable: #9CDCFE",
"light_plus": "variable: #001080",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "variable: #9CDCFE"
}
},
{
"c": "}",
"t": "source.r meta.block.r punctuation.section.block.end.r",
"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.r",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "1",
"t": "source.r constant.numeric.r",
"r": {
"dark_plus": "constant.numeric: #B5CEA8",
"light_plus": "constant.numeric: #09885A",
"dark_vs": "constant.numeric: #B5CEA8",
"light_vs": "constant.numeric: #09885A",
"hc_black": "constant.numeric: #B5CEA8"
}
},
{
"c": ", ",
"t": "source.r",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "-",
"t": "source.r keyword.operator.arithmetic.r",
"r": {
"dark_plus": "keyword.operator: #D4D4D4",
@@ -440,8 +495,63 @@
}
},
{
"c": "2",
"t": "source.r constant.numeric.r",
"c": " ",
"t": "source.r",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "y",
"t": "source.r variable.other.r",
"r": {
"dark_plus": "variable: #9CDCFE",
"light_plus": "variable: #001080",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "variable: #9CDCFE"
}
},
{
"c": "}",
"t": "source.r punctuation.section.braces.end.r",
"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.r meta.function-call.r variable.function.r",
"r": {
"dark_plus": "variable: #9CDCFE",
"light_plus": "variable: #001080",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "variable: #9CDCFE"
}
},
{
"c": "(",
"t": "source.r meta.function-call.r punctuation.section.parens.begin.r",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "1",
"t": "source.r meta.function-call.r meta.function-call.arguments.r constant.numeric.float.decimal.r",
"r": {
"dark_plus": "constant.numeric: #B5CEA8",
"light_plus": "constant.numeric: #09885A",
@@ -451,8 +561,63 @@
}
},
{
"c": ", ",
"t": "source.r",
"c": ",",
"t": "source.r meta.function-call.r meta.function-call.arguments.r punctuation.separator.parameters.r",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": " ",
"t": "source.r meta.function-call.r meta.function-call.arguments.r",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "-",
"t": "source.r meta.function-call.r meta.function-call.arguments.r keyword.operator.arithmetic.r",
"r": {
"dark_plus": "keyword.operator: #D4D4D4",
"light_plus": "keyword.operator: #000000",
"dark_vs": "keyword.operator: #D4D4D4",
"light_vs": "keyword.operator: #000000",
"hc_black": "keyword.operator: #D4D4D4"
}
},
{
"c": "2",
"t": "source.r meta.function-call.r meta.function-call.arguments.r constant.numeric.float.decimal.r",
"r": {
"dark_plus": "constant.numeric: #B5CEA8",
"light_plus": "constant.numeric: #09885A",
"dark_vs": "constant.numeric: #B5CEA8",
"light_vs": "constant.numeric: #09885A",
"hc_black": "constant.numeric: #B5CEA8"
}
},
{
"c": ",",
"t": "source.r meta.function-call.r meta.function-call.arguments.r punctuation.separator.parameters.r",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": " ",
"t": "source.r meta.function-call.r meta.function-call.arguments.r",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@@ -463,7 +628,7 @@
},
{
"c": "2.0",
"t": "source.r constant.numeric.r",
"t": "source.r meta.function-call.r meta.function-call.arguments.r constant.numeric.float.decimal.r",
"r": {
"dark_plus": "constant.numeric: #B5CEA8",
"light_plus": "constant.numeric: #09885A",
@@ -474,7 +639,7 @@
},
{
"c": ")",
"t": "source.r",
"t": "source.r meta.function-call.r punctuation.definition.parameters.r",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@@ -484,8 +649,19 @@
}
},
{
"c": "add(",
"t": "source.r",
"c": "add",
"t": "source.r meta.function-call.r variable.function.r",
"r": {
"dark_plus": "variable: #9CDCFE",
"light_plus": "variable: #001080",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "variable: #9CDCFE"
}
},
{
"c": "(",
"t": "source.r meta.function-call.r punctuation.section.parens.begin.r",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@@ -496,7 +672,7 @@
},
{
"c": "1.0e10",
"t": "source.r constant.numeric.r",
"t": "source.r meta.function-call.r meta.function-call.arguments.r constant.numeric.float.decimal.r",
"r": {
"dark_plus": "constant.numeric: #B5CEA8",
"light_plus": "constant.numeric: #09885A",
@@ -506,8 +682,19 @@
}
},
{
"c": ", ",
"t": "source.r",
"c": ",",
"t": "source.r meta.function-call.r meta.function-call.arguments.r punctuation.separator.parameters.r",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": " ",
"t": "source.r meta.function-call.r meta.function-call.arguments.r",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@@ -518,7 +705,7 @@
},
{
"c": "2.0e10",
"t": "source.r constant.numeric.r",
"t": "source.r meta.function-call.r meta.function-call.arguments.r constant.numeric.float.decimal.r",
"r": {
"dark_plus": "constant.numeric: #B5CEA8",
"light_plus": "constant.numeric: #09885A",
@@ -529,7 +716,7 @@
},
{
"c": ")",
"t": "source.r",
"t": "source.r meta.function-call.r punctuation.definition.parameters.r",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@@ -539,7 +726,18 @@
}
},
{
"c": "paste(",
"c": "paste",
"t": "source.r support.function.r",
"r": {
"dark_plus": "support.function: #DCDCAA",
"light_plus": "support.function: #795E26",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "support.function: #DCDCAA"
}
},
{
"c": "(",
"t": "source.r",
"r": {
"dark_plus": "default: #D4D4D4",
@@ -616,7 +814,18 @@
}
},
{
"c": "paste(",
"c": "paste",
"t": "source.r support.function.r",
"r": {
"dark_plus": "support.function: #DCDCAA",
"light_plus": "support.function: #795E26",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "support.function: #DCDCAA"
}
},
{
"c": "(",
"t": "source.r",
"r": {
"dark_plus": "default: #D4D4D4",
@@ -693,7 +902,18 @@
}
},
{
"c": "paste(",
"c": "paste",
"t": "source.r support.function.r",
"r": {
"dark_plus": "support.function: #DCDCAA",
"light_plus": "support.function: #795E26",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "support.function: #DCDCAA"
}
},
{
"c": "(",
"t": "source.r",
"r": {
"dark_plus": "default: #D4D4D4",
-1
View File
@@ -1 +0,0 @@
test/**
-22
View File
@@ -1,22 +0,0 @@
// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS:
[{
"name": "textmate/ruby.tmbundle",
"version": "0.0.0",
"license": "TextMate Bundle License",
"repositoryURL": "https://github.com/textmate/ruby.tmbundle",
"licenseDetail": [
"Copyright (c) textmate-ruby.tmbundle 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\"."
]
}]
@@ -1,29 +0,0 @@
{
"comments": {
"lineComment": "#",
"blockComment": [ "=begin", "=end" ]
},
"brackets": [
["{", "}"],
["[", "]"],
["(", ")"]
],
"autoClosingPairs": [
["{", "}"],
["[", "]"],
["(", ")"],
["\"", "\""],
["'", "'"]
],
"surroundingPairs": [
["{", "}"],
["[", "]"],
["(", ")"],
["\"", "\""],
["'", "'"]
],
"indentationRules": {
"increaseIndentPattern": "^\\s*((begin|class|def|else|elsif|ensure|for|if|module|rescue|unless|until|when|while)|(.*\\sdo\\b))\\b[^\\{;]*$",
"decreaseIndentPattern": "^\\s*([}\\]]([,)]?\\s*(#|$)|\\.[a-zA-Z_]\\w*\\b)|(end|rescue|ensure|else|elsif|when)\\b)"
}
}
-24
View File
@@ -1,24 +0,0 @@
{
"name": "ruby",
"version": "0.2.1",
"publisher": "vscode",
"engines": { "vscode": "*" },
"scripts": {
"update-grammar": "node ../../build/npm/update-grammar.js textmate/ruby.tmbundle Syntaxes/Ruby.plist ./syntaxes/ruby.tmLanguage.json"
},
"contributes": {
"languages": [{
"id": "ruby",
"extensions": [ ".rb", ".rbx", ".rjs", ".gemspec", ".rake", ".ru", ".erb" ],
"filenames": [ "rakefile", "gemfile", "guardfile", "podfile", "capfile" ],
"aliases": [ "Ruby", "rb" ],
"firstLine": "^#!/.*\\bruby\\b",
"configuration": "./language-configuration.json"
}],
"grammars": [{
"language": "ruby",
"scopeName": "source.ruby",
"path": "./syntaxes/ruby.tmLanguage.json"
}]
}
}
File diff suppressed because it is too large Load Diff
@@ -1,46 +0,0 @@
# encoding: utf-8
# Code generated by Microsoft (R) AutoRest Code Generator 0.16.0.0
# Changes may cause incorrect behavior and will be lost if the code is
# regenerated.
module Azure::ARM::Scheduler
#
# A service client - single point of access to the REST API.
#
class SchedulerManagementClient < MsRestAzure::AzureServiceClient
include Azure::ARM::Scheduler::Models
include MsRestAzure
# @return job_collections
attr_reader :job_collections
#
# Creates initializes a new instance of the SchedulerManagementClient class.
# @param credentials [MsRest::ServiceClientCredentials] credentials to authorize HTTP requests made by the service client.
# @param base_url [String] the base URI of the service.
# @param options [Array] filters to be applied to the HTTP requests.
#
def initialize(credentials, base_url = nil, options = nil)
super(credentials, options)
@base_url = base_url || 'https://management.azure.com'
fail ArgumentError, 'credentials is nil' if credentials.nil?
fail ArgumentError, 'invalid type of credentials input parameter' unless credentials.is_a?(MsRest::ServiceClientCredentials)
@credentials = credentials
@job_collections = JobCollections.new(self)
@jobs = Jobs.new(self)
@api_version = '2016-01-01'
@long_running_operation_retry_timeout = 30
@generate_client_request_id = true
if MacOS.version >= :mavericks
version = `#{MAVERICKS_PKG_PATH}/usr/bin/clang --version`
else
version = `/usr/bin/clang --version`
end
version = version[/clang-(\d+\.\d+\.\d+(\.\d+)?)/, 1] || "0"
version < latest_version
end
end
end
File diff suppressed because it is too large Load Diff
@@ -4,7 +4,7 @@
"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/shellscript.tmbundle/commit/ba95d7b742caef130911d878f42f66bdd80181e4",
"version": "https://github.com/textmate/shellscript.tmbundle/commit/1c0cc0b904bb87b18b6987109e694f9d0058656d",
"fileTypes": [
"sh",
"bash",
@@ -771,6 +771,16 @@
"match": "\\\\[`\\\\$]",
"name": "constant.character.escape.shell"
},
{
"begin": "(?<=^|;|&|\\s|`)(#)(?!\\{)",
"beginCaptures": {
"1": {
"name": "punctuation.definition.comment.shell"
}
},
"end": "(?=`)|\\n",
"name": "comment.line.number-sign.shell"
},
{
"include": "$self"
}
@@ -791,6 +801,16 @@
},
"name": "string.interpolated.dollar.shell",
"patterns": [
{
"begin": "(?<=^|;|&|\\s|\\()(#)(?!\\{)",
"beginCaptures": {
"1": {
"name": "punctuation.definition.comment.shell"
}
},
"end": "(?=\\))|\\n",
"name": "comment.line.number-sign.shell"
},
{
"include": "$self"
}
@@ -848,7 +868,7 @@
"name": "keyword.control.shell"
}
},
"end": "(?<=^|;|&|\\s)(done)(?=\\s|;|&|$)",
"end": "(?<=^|;|&|\\s)(done)(?=\\s|;|&|$|\\))",
"name": "meta.scope.for-loop.shell",
"patterns": [
{
@@ -866,7 +886,7 @@
"name": "variable.other.loop.shell"
}
},
"end": "(?<=^|;|&|\\s)(done)(?=\\s|;|&|$)",
"end": "(?<=^|;|&|\\s)(done)(?=\\s|;|&|$|\\))",
"endCaptures": {
"1": {
"name": "keyword.control.shell"
@@ -886,7 +906,7 @@
"name": "keyword.control.shell"
}
},
"end": "(?<=^|;|&|\\s)(done)(?=\\s|;|&|$)",
"end": "(?<=^|;|&|\\s)(done)(?=\\s|;|&|$|\\))",
"name": "meta.scope.while-loop.shell",
"patterns": [
{
@@ -904,7 +924,7 @@
"name": "variable.other.loop.shell"
}
},
"end": "(?<=^|;|&|\\s)(done)(?=\\s|;|&|$)",
"end": "(?<=^|;|&|\\s)(done)(?=\\s|;|&|$|\\))",
"endCaptures": {
"1": {
"name": "keyword.control.shell"
@@ -924,7 +944,7 @@
"name": "keyword.control.shell"
}
},
"end": "(?<=^|;|&|\\s)(esac)(?=\\s|;|&|$)",
"end": "(?<=^|;|&|\\s)(esac)(?=\\s|;|&|$|\\))",
"name": "meta.scope.case-block.shell",
"patterns": [
{
@@ -934,7 +954,7 @@
"name": "keyword.control.shell"
}
},
"end": "(?<=^|;|&|\\s)(?=(?:esac)(?:\\s|;|&|$))",
"end": "(?<=^|;|&|\\s)(?=(?:esac)(?:\\s|;|&|$|\\)))",
"name": "meta.scope.case-body.shell",
"patterns": [
{
@@ -961,7 +981,7 @@
}
},
"comment": "Restrict match to avoid matching in lines like `dd if=/dev/sda1 …`",
"end": "(?<=^|;|&|\\s)(fi)(?=\\s|;|&|$)",
"end": "(?<=^|;|&|\\s)(fi)(?=\\s|;|&|$|\\))",
"endCaptures": {
"1": {
"name": "keyword.control.shell"
+3 -18
View File
@@ -1,23 +1,8 @@
// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS:
[{
"name": "textmate/sql.tmbundle",
"name": "Microsoft/vscode-mssql",
"version": "0.0.0",
"license": "TextMate Bundle License",
"repositoryURL": "https://github.com/textmate/sql.tmbundle",
"licenseDetail": [
"Copyright (c) textmate-sql.tmbundle 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\"."
]
"license": "MIT",
"repositoryURL": "https://github.com/Microsoft/vscode-mssql"
}]
+4 -1
View File
@@ -3,6 +3,9 @@
"version": "0.1.0",
"publisher": "vscode",
"engines": { "vscode": "*" },
"scripts": {
"update-grammar": "node ../../build/npm/update-grammar.js Microsoft/vscode-mssql syntaxes/SQL.plist ./syntaxes/sql.tmLanguage.json"
},
"contributes": {
"languages": [{
"id": "sql",
@@ -13,7 +16,7 @@
"grammars": [{
"language": "sql",
"scopeName": "source.sql",
"path": "./syntaxes/SQL.plist"
"path": "./syntaxes/sql.tmLanguage.json"
}]
}
}
File diff suppressed because one or more lines are too long
@@ -1,7 +1,7 @@
[
{
"c": "CREATE",
"t": "source.sql meta.create.sql keyword.other.create.sql",
"t": "source.sql keyword.other.sql",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
@@ -11,51 +11,7 @@
}
},
{
"c": " ",
"t": "source.sql meta.create.sql",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "VIEW",
"t": "source.sql meta.create.sql keyword.other.sql",
"r": {
"dark_plus": "keyword: #569CD6",
"light_plus": "keyword: #0000FF",
"dark_vs": "keyword: #569CD6",
"light_vs": "keyword: #0000FF",
"hc_black": "keyword: #569CD6"
}
},
{
"c": " ",
"t": "source.sql meta.create.sql",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "default: #FFFFFF"
}
},
{
"c": "METRIC_STATS",
"t": "source.sql meta.create.sql entity.name.function.sql",
"r": {
"dark_plus": "entity.name.function: #DCDCAA",
"light_plus": "entity.name.function: #795E26",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "entity.name.function: #DCDCAA"
}
},
{
"c": " (ID, MONTH, TEMP_C, RAIN_C) ",
"c": " VIEW METRIC_STATS (ID, MONTH, TEMP_C, RAIN_C) ",
"t": "source.sql",
"r": {
"dark_plus": "default: #D4D4D4",
@@ -7,6 +7,13 @@
"foreground": "#6688cc"
}
},
{
"scope": ["meta.embedded", "source.groovy.embedded"],
"settings": {
"background": "#000c18",
"foreground": "#6688cc"
}
},
{
"name": "Comment",
"scope": "comment",
@@ -1,12 +1,13 @@
{
"name": "SQL Operations Studio-dark-theme",
"type": "dark",
"$schema": "vscode://schemas/color-theme",
"name": "SQL Operations Studio-dark-theme",
"type": "dark",
"colors": {
// base
"foreground": "#fffffe",
"focusBorder": "#0E639C",
"selection.background": "#3062d6",
"selection.background": "#3062d6",
//text colors
"textLinkForeground": "#30B4FF",
@@ -42,8 +43,8 @@
//List and trees
"list.activeSelectionBackground": "#3062d6",
"list.hoverBackground": "#444444",
"pickerGroup.border": "#00BCF2",
"activityBar.background": "#444444",
"pickerGroup.border": "#00BCF2",
"activityBar.background": "#444444",
"sideBar.background": "#333333",
"editorGroupHeader.tabsBackground": "#444444",
"editor.background": "#212121",
@@ -52,11 +53,11 @@
"editorLink.activeForeground": "#30B4FF",
"editorGroup.border": "#333333",
"editorGroup.background": "#212121",
"tab.activeBackground": "#212121",
"tab.activeBackground": "#212121",
"tab.activeForeground": "#ffffff",
"tab.inactiveBackground": "#444444",
"tab.inactiveForeground": "#888888",
"tab.border": "#3c3c3c",
"tab.border": "#3c3c3c",
"panel.background": "#212121",
"panel.border": "#515151",
"panelTitle.activeForeground": "#ffffff",
@@ -65,8 +66,7 @@
"tokenColors": [
{
"settings": {
"foreground": "#ffffffff",
"background": "#212121ff"
"foreground": "#ffffffff"
}
},
{
@@ -1,4 +1,5 @@
{
"$schema": "vscode://schemas/color-theme",
"name": "SQL Operations Studio-light-theme",
"type": "light",
"colors": {
@@ -34,7 +35,7 @@
"badge.foreground": "#ffffff",
//Input Control
"input.background": "#EAEAEA00",
"input.background": "#fffffe",
"input.border": "#c8c8c8",
"input.disabled.background": "#dcdcdc",
"input.disabled.foreground": "#888888",
@@ -56,7 +57,7 @@
"editorGroupHeader.tabsBackground": "#f4f4f4",
"editor.background": "#fffffe",
"editor.foreground": "#212121",
"editorWidget.background": "#C8C8C8",
"editorWidget.background": "#dcdcdc",
"editorLink.activeForeground": "#3062D6",
"editorGroup.border": "#f4f4f4",
"editorGroup.background": "#fffffe",
@@ -77,8 +78,7 @@
"tokenColors": [
{
"settings": {
"foreground": "#212121ff",
"background": "#fffffeff"
"foreground": "#212121ff"
}
},
{
+78 -242
View File
@@ -1,273 +1,109 @@
{
"name": "Dark Visual Studio",
"settings": [
"$schema": "vscode://schemas/color-theme",
"name": "Dark High Contrast",
"include": "./hc_black_defaults.json",
"colors": {
"selection.background": "#008000",
"editor.selectionBackground": "#FFFFFF"
},
"tokenColors": [
{
"scope": "emphasis",
"name": "Function declarations",
"scope": [
"entity.name.function",
"support.function",
"support.constant.handlebars"
],
"settings": {
"fontStyle": "italic"
"foreground": "#DCDCAA"
}
},
{
"scope": "strong",
"name": "Types declaration and references",
"scope": [
"meta.return-type",
"support.class",
"support.type",
"entity.name.type",
"entity.name.class",
"storage.type.cs",
"storage.type.generic.cs",
"storage.type.modifier.cs",
"storage.type.variable.cs",
"storage.type.annotation.java",
"storage.type.generic.java",
"storage.type.java",
"storage.type.object.array.java",
"storage.type.primitive.array.java",
"storage.type.primitive.java",
"storage.type.token.java",
"storage.type.groovy",
"storage.type.annotation.groovy",
"storage.type.parameters.groovy",
"storage.type.generic.groovy",
"storage.type.object.array.groovy",
"storage.type.primitive.array.groovy",
"storage.type.primitive.groovy"
],
"settings": {
"fontStyle": "bold"
"foreground": "#4EC9B0"
}
},
{
"scope": "header",
"name": "Types declaration and references, TS grammar specific",
"scope": [
"meta.type.cast.expr",
"meta.type.new.expr",
"support.constant.math",
"support.constant.dom",
"support.constant.json",
"entity.other.inherited-class"
],
"settings": {
"foreground": "#000080"
}
},
{
"scope": "comment",
"settings": {
"foreground": "#7ca668"
}
},
{
"scope": "constant.language",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "constant.numeric",
"settings": {
"foreground": "#b5cea8"
}
},
{
"scope": "constant.regexp",
"settings": {
"foreground": "#b46695"
}
},
{
"scope": "constant.rgb-value",
"settings": {
"foreground": "#d4d4d4"
}
},
{
"scope": "entity.name.tag",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "entity.name.selector",
"settings": {
"foreground": "#d7ba7d"
}
},
{
"scope": "entity.other.attribute-name",
"settings": {
"foreground": "#9cdcfe"
}
},
{
"scope": "entity.other.attribute-name.css",
"settings": {
"foreground": "#d7ba7d"
}
},
{
"scope": "invalid",
"settings": {
"foreground": "#f44747"
}
},
{
"scope": "markup.underline",
"settings": {
"fontStyle": "underline"
}
},
{
"scope": "markup.bold",
"settings": {
"fontStyle": "bold"
}
},
{
"scope": "markup.heading",
"settings": {
"foreground": "#6796e6"
}
},
{
"scope": "markup.italic",
"settings": {
"fontStyle": "italic"
}
},
{
"scope": "markup.inserted",
"settings": {
"foreground": "#b5cea8"
}
},
{
"scope": "markup.deleted",
"settings": {
"foreground": "#ce9178"
}
},
{
"scope": "markup.changed",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "meta.selector",
"settings": {
"foreground": "#d7ba7d"
}
},
{
"name": "brackets of XML/HTML tags",
"scope": "punctuation.definition.tag",
"settings": {
"foreground": "#808080"
}
},
{
"scope": "meta.preprocessor",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "meta.preprocessor.string",
"settings": {
"foreground": "#ce9178"
}
},
{
"scope": "meta.preprocessor.numeric",
"settings": {
"foreground": "#b5cea8"
}
},
{
"scope": "meta.structure.dictionary.key.python",
"settings": {
"foreground": "#9cdcfe"
}
},
{
"scope": "storage",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "storage.type",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "storage.modifier",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "string",
"settings": {
"foreground": "#ce9178"
}
},
{
"scope": "string.tag",
"settings": {
"foreground": "#ce9178"
}
},
{
"scope": "string.value",
"settings": {
"foreground": "#ce9178"
}
},
{
"scope": "string.regexp",
"settings": {
"foreground": "#d16969"
}
},
{
"name": "JavaScript string interpolation ${}",
"scope": "string.template-expression",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "support.type.property-name",
"settings": {
"foreground": "#d4d4d4"
}
},
{
"scope": "keyword",
"settings": {
"foreground": "#569cd6"
"foreground": "#4EC9B0"
}
},
{
"name": "Control flow keywords",
"scope": "keyword.control",
"settings": {
"foreground": "#569cd6"
"foreground": "#C586C0"
}
},
{
"scope": "keyword.operator",
"name": "Variable and parameter name",
"scope": [
"variable",
"meta.definition.variable.name",
"support.variable"
],
"settings": {
"foreground": "#d4d4d4"
"foreground": "#9CDCFE"
}
},
{
"scope": ["keyword.operator.new", "keyword.operator.expression"],
"name": "Object keys, TS grammar specific",
"scope": [
"meta.object-literal.key",
"meta.object-literal.key entity.name.function"
],
"settings": {
"foreground": "#569cd6"
"foreground": "#9CDCFE"
}
},
{
"scope": "keyword.other.unit",
"name": "CSS property value",
"scope": [
"support.constant.property-value",
"support.constant.font-name",
"support.constant.media-type",
"support.constant.media",
"constant.other.color.rgb-value",
"constant.other.rgb-value",
"support.constant.color"
],
"settings": {
"foreground": "#b5cea8"
}
},
{
"scope": "support.function.git-rebase",
"settings": {
"foreground": "#d4d4d4"
}
},
{
"scope": "constant.sha.git-rebase",
"settings": {
"foreground": "#b5cea8"
}
},
{
"name": "coloring of the Java import and package identifiers",
"scope": ["storage.modifier.import.java", "storage.modifier.package.java"],
"settings": {
"foreground": "#d4d4d4"
}
},
{
"name": "coloring of the TS this",
"scope": "variable.this",
"settings": {
"foreground": "#569cd6"
"foreground": "#CE9178"
}
}
]
@@ -0,0 +1,345 @@
{
"$schema": "vscode://schemas/color-theme",
"name": "High Contrast Default Colors",
"colors": {
"editor.background": "#000000",
"editor.foreground": "#FFFFFF",
"editorIndentGuide.background": "#FFFFFF",
"sideBarTitle.foreground": "#FFFFFF"
},
"settings": [
{
"settings": {
"foreground": "#FFFFFF",
"background": "#000000"
}
},
{
"scope": [
"meta.embedded",
"source.groovy.embedded"
],
"settings": {
"foreground": "#FFFFFF",
"background": "#000000"
}
},
{
"scope": "emphasis",
"settings": {
"fontStyle": "italic"
}
},
{
"scope": "strong",
"settings": {
"fontStyle": "bold"
}
},
{
"scope": "meta.diff.header",
"settings": {
"foreground": "#000080"
}
},
{
"scope": "comment",
"settings": {
"foreground": "#7ca668"
}
},
{
"scope": "constant.language",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": [
"constant.numeric",
"constant.other.color.rgb-value",
"constant.other.rgb-value",
"support.constant.color"
],
"settings": {
"foreground": "#b5cea8"
}
},
{
"scope": "constant.regexp",
"settings": {
"foreground": "#b46695"
}
},
{
"scope": "constant.character",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "entity.name.tag",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "entity.name.tag.css",
"settings": {
"foreground": "#d7ba7d"
}
},
{
"scope": "entity.other.attribute-name",
"settings": {
"foreground": "#9cdcfe"
}
},
{
"scope": [
"entity.other.attribute-name.class.css",
"entity.other.attribute-name.class.mixin.css",
"entity.other.attribute-name.id.css",
"entity.other.attribute-name.parent-selector.css",
"entity.other.attribute-name.pseudo-class.css",
"entity.other.attribute-name.pseudo-element.css",
"source.css.less entity.other.attribute-name.id",
"entity.other.attribute-name.attribute.scss",
"entity.other.attribute-name.scss"
],
"settings": {
"foreground": "#d7ba7d"
}
},
{
"scope": "invalid",
"settings": {
"foreground": "#f44747"
}
},
{
"scope": "markup.underline",
"settings": {
"fontStyle": "underline"
}
},
{
"scope": "markup.bold",
"settings": {
"fontStyle": "bold"
}
},
{
"scope": "markup.heading",
"settings": {
"foreground": "#6796e6"
}
},
{
"scope": "markup.italic",
"settings": {
"fontStyle": "italic"
}
},
{
"scope": "markup.inserted",
"settings": {
"foreground": "#b5cea8"
}
},
{
"scope": "markup.deleted",
"settings": {
"foreground": "#ce9178"
}
},
{
"scope": "markup.changed",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "meta.selector",
"settings": {
"foreground": "#d7ba7d"
}
},
{
"name": "brackets of XML/HTML tags",
"scope": [
"punctuation.definition.tag"
],
"settings": {
"foreground": "#808080"
}
},
{
"scope": "meta.preprocessor",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "meta.preprocessor.string",
"settings": {
"foreground": "#ce9178"
}
},
{
"scope": "meta.preprocessor.numeric",
"settings": {
"foreground": "#b5cea8"
}
},
{
"scope": "meta.structure.dictionary.key.python",
"settings": {
"foreground": "#9cdcfe"
}
},
{
"scope": "storage",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "storage.type",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "storage.modifier",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "string",
"settings": {
"foreground": "#ce9178"
}
},
{
"scope": "string.tag",
"settings": {
"foreground": "#ce9178"
}
},
{
"scope": "string.value",
"settings": {
"foreground": "#ce9178"
}
},
{
"scope": "string.regexp",
"settings": {
"foreground": "#d16969"
}
},
{
"name": "String interpolation",
"scope": [
"punctuation.definition.template-expression.begin",
"punctuation.definition.template-expression.end",
"punctuation.section.embedded"
],
"settings": {
"foreground": "#569cd6"
}
},
{
"name": "Reset JavaScript string interpolation expression",
"scope": [
"meta.template.expression"
],
"settings": {
"foreground": "#ffffff"
}
},
{
"scope": [
"support.type.vendored.property-name",
"support.type.property-name",
"variable.css",
"variable.scss",
"variable.other.less",
"source.coffee.embedded"
],
"settings": {
"foreground": "#d4d4d4"
}
},
{
"scope": "keyword",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "keyword.control",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "keyword.operator",
"settings": {
"foreground": "#d4d4d4"
}
},
{
"scope": [
"keyword.operator.new",
"keyword.operator.expression",
"keyword.operator.cast",
"keyword.operator.sizeof",
"keyword.operator.logical.python"
],
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "keyword.other.unit",
"settings": {
"foreground": "#b5cea8"
}
},
{
"scope": "support.function.git-rebase",
"settings": {
"foreground": "#d4d4d4"
}
},
{
"scope": "constant.sha.git-rebase",
"settings": {
"foreground": "#b5cea8"
}
},
{
"name": "coloring of the Java import and package identifiers",
"scope": [
"storage.modifier.import.java",
"variable.language.wildcard.java",
"storage.modifier.package.java"
],
"settings": {
"foreground": "#d4d4d4"
}
},
{
"name": "coloring of the TS this",
"scope": "variable.language.this",
"settings": {
"foreground": "#569cd6"
}
}
]
}
@@ -54,6 +54,13 @@
"foreground": "#d3af86"
}
},
{
"scope": ["meta.embedded", "source.groovy.embedded"],
"settings": {
"background": "#221a0f",
"foreground": "#d3af86"
}
},
{
"name": "Text",
"scope": "variable.parameter.function",

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