Merge remote-tracking branch 'origin/master' into alex/minimap

This commit is contained in:
Alex Dima
2017-02-16 23:26:51 +01:00
157 changed files with 6802 additions and 6277 deletions
+10 -3
View File
@@ -7,11 +7,17 @@
"name": "Unit Tests",
"protocol": "legacy",
"program": "${workspaceRoot}/node_modules/mocha/bin/_mocha",
"runtimeExecutable": "${execPath}",
"runtimeExecutable": "${workspaceRoot}/.build/electron/Code - OSS.app/Contents/MacOS/Electron",
"windows": {
"runtimeExecutable": "${workspaceRoot}/.build/electron/Code - OSS.exe"
},
"linux": {
"runtimeExecutable": "${workspaceRoot}/.build/electron/code-oss"
},
"stopOnEntry": false,
"args": [
"--timeout",
"999999"
"2000"
],
"cwd": "${workspaceRoot}",
"env": {
@@ -20,7 +26,8 @@
"sourceMaps": true,
"outFiles": [
"${workspaceRoot}/out/**/*.js"
]
],
"preLaunchTask": "electron"
},
{
"type": "node",
+8 -1
View File
@@ -47,7 +47,7 @@
],
"severity": "warning",
"pattern": {
"regexp": "(.*)\\[(\\d+),\\s(\\d+)\\]:\\s(.*)$", // (.*)\[(\d+), (\d+)\]: (.*)
"regexp": "(.*)\\[(\\d+),\\s(\\d+)\\]:\\s(.*)$", // (.*)\[(\d+), (\d+)\]: (.*)
"file": 1,
"line": 2,
"column": 3,
@@ -62,6 +62,13 @@
],
"showOutput": "always",
"isTestCommand": true
},
{
"taskName": "electron",
"args": [
"--no-color"
],
"showOutput": "never"
}
]
}
+2 -2
View File
@@ -40,8 +40,8 @@ const nodeModules = ['electron', 'original-fs']
// Build
const builtInExtensions = [
{ name: 'ms-vscode.node-debug', version: '1.10.6' },
{ name: 'ms-vscode.node-debug2', version: '1.9.13' }
{ name: 'ms-vscode.node-debug', version: '1.10.12' },
{ name: 'ms-vscode.node-debug2', version: '1.10.0' }
];
const vscodeEntryPoints = _.flatten([
+1
View File
@@ -44,6 +44,7 @@ function buildWin32Setup(cb) {
NameVersion: product.win32NameVersion,
ExeBasename: product.nameShort,
RegValueName: product.win32RegValueName,
ShellNameShort: product.win32ShellNameShort,
AppMutex: product.win32MutexName,
AppId: product.win32AppId,
AppUserId: product.win32AppUserModelId,
+4 -4
View File
@@ -772,16 +772,16 @@ Root: HKCR; Subkey: "{#RegValueName}SourceFile\shell\open\command"; ValueType: s
Root: HKCU; Subkey: "Environment"; ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}\bin"; Tasks: addtopath; Check: NeedsAddPath(ExpandConstant('{app}\bin'))
Root: HKCU; Subkey: "SOFTWARE\Classes\*\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "Open with {#NameShort}"; Tasks: addcontextmenufiles; Flags: uninsdeletekey
Root: HKCU; Subkey: "SOFTWARE\Classes\*\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "Open with {#ShellNameShort}"; Tasks: addcontextmenufiles; Flags: uninsdeletekey
Root: HKCU; Subkey: "SOFTWARE\Classes\*\shell\{#RegValueName}"; ValueType: expandsz; ValueName: "Icon"; ValueData: "{app}\{#ExeBasename}.exe"; Tasks: addcontextmenufiles
Root: HKCU; Subkey: "SOFTWARE\Classes\*\shell\{#RegValueName}\command"; ValueType: expandsz; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: addcontextmenufiles
Root: HKCU; Subkey: "SOFTWARE\Classes\directory\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "Open with {#NameShort}"; Tasks: addcontextmenufolders; Flags: uninsdeletekey
Root: HKCU; Subkey: "SOFTWARE\Classes\directory\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "Open with {#ShellNameShort}"; Tasks: addcontextmenufolders; Flags: uninsdeletekey
Root: HKCU; Subkey: "SOFTWARE\Classes\directory\shell\{#RegValueName}"; ValueType: expandsz; ValueName: "Icon"; ValueData: "{app}\{#ExeBasename}.exe"; Tasks: addcontextmenufolders
Root: HKCU; Subkey: "SOFTWARE\Classes\directory\shell\{#RegValueName}\command"; ValueType: expandsz; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%V"""; Tasks: addcontextmenufolders
Root: HKCU; Subkey: "SOFTWARE\Classes\directory\background\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "Open with {#NameShort}"; Tasks: addcontextmenufolders; Flags: uninsdeletekey
Root: HKCU; Subkey: "SOFTWARE\Classes\directory\background\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "Open with {#ShellNameShort}"; Tasks: addcontextmenufolders; Flags: uninsdeletekey
Root: HKCU; Subkey: "SOFTWARE\Classes\directory\background\shell\{#RegValueName}"; ValueType: expandsz; ValueName: "Icon"; ValueData: "{app}\{#ExeBasename}.exe"; Tasks: addcontextmenufolders
Root: HKCU; Subkey: "SOFTWARE\Classes\directory\background\shell\{#RegValueName}\command"; ValueType: expandsz; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%V"""; Tasks: addcontextmenufolders
Root: HKCU; Subkey: "SOFTWARE\Classes\Drive\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "Open with {#NameShort}"; Tasks: addcontextmenufolders; Flags: uninsdeletekey
Root: HKCU; Subkey: "SOFTWARE\Classes\Drive\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "Open with {#ShellNameShort}"; Tasks: addcontextmenufolders; Flags: uninsdeletekey
Root: HKCU; Subkey: "SOFTWARE\Classes\Drive\shell\{#RegValueName}"; ValueType: expandsz; ValueName: "Icon"; ValueData: "{app}\{#ExeBasename}.exe"; Tasks: addcontextmenufolders
Root: HKCU; Subkey: "SOFTWARE\Classes\Drive\shell\{#RegValueName}\command"; ValueType: expandsz; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%V"""; Tasks: addcontextmenufolders
+152 -36
View File
@@ -21,6 +21,15 @@
},
"contributes": {
"commands": [
{
"command": "git.init",
"title": "%command.init%",
"category": "Git",
"icon": {
"light": "resources/icons/light/git.svg",
"dark": "resources/icons/dark/git.svg"
}
},
{
"command": "git.refresh",
"title": "%command.refresh%",
@@ -191,189 +200,296 @@
}
],
"menus": {
"commandPalette": [
{
"command": "git.init",
"when": "scmProvider == git && scmProviderState == norepo"
},
{
"command": "git.refresh",
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.openChange",
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.openFile",
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.stage",
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.stageAll",
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.unstage",
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.unstageAll",
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.clean",
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.cleanAll",
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.commit",
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.commitStaged",
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.commitStagedSigned",
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.commitAll",
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.commitAllSigned",
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.undoCommit",
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.checkout",
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.branch",
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.pull",
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.pullRebase",
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.push",
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.pushTo",
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.sync",
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.publish",
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.showOutput",
"when": "scmProvider == git && scmProviderState == idle"
}
],
"scm/title": [
{
"command": "git.init",
"group": "navigation",
"when": "scmProvider == git && scmProviderState == norepo"
},
{
"command": "git.commit",
"group": "navigation",
"when": "scmProvider == git"
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.refresh",
"group": "navigation",
"when": "scmProvider == git"
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.sync",
"group": "1_sync",
"when": "scmProvider == git"
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.pull",
"group": "1_sync",
"when": "scmProvider == git"
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.pullRebase",
"group": "1_sync",
"when": "scmProvider == git"
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.push",
"group": "1_sync",
"when": "scmProvider == git"
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.pushTo",
"group": "1_sync",
"when": "scmProvider == git"
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.publish",
"group": "2_publish",
"when": "scmProvider == git"
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.commitStaged",
"group": "3_commit",
"when": "scmProvider == git"
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.commitStagedSigned",
"group": "3_commit",
"when": "scmProvider == git"
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.commitAll",
"group": "3_commit",
"when": "scmProvider == git"
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.commitAllSigned",
"group": "3_commit",
"when": "scmProvider == git"
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.undoCommit",
"group": "3_commit",
"when": "scmProvider == git"
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.unstageAll",
"group": "4_stage",
"when": "scmProvider == git"
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.cleanAll",
"group": "4_stage",
"when": "scmProvider == git"
"when": "scmProvider == git && scmProviderState == idle"
},
{
"command": "git.showOutput",
"group": "5_output",
"when": "scmProvider == git"
"when": "scmProvider == git && scmProviderState == idle"
}
],
"scm/resourceGroup/context": [
{
"command": "git.stageAll",
"when": "scmProvider == git && scmResourceGroup == merge",
"when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == merge",
"group": "1_modification"
},
{
"command": "git.stageAll",
"when": "scmProvider == git && scmResourceGroup == merge",
"when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == merge",
"group": "inline"
},
{
"command": "git.unstageAll",
"when": "scmProvider == git && scmResourceGroup == index",
"when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == index",
"group": "1_modification"
},
{
"command": "git.unstageAll",
"when": "scmProvider == git && scmResourceGroup == index",
"when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == index",
"group": "inline"
},
{
"command": "git.cleanAll",
"when": "scmProvider == git && scmResourceGroup == workingTree",
"when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == workingTree",
"group": "1_modification"
},
{
"command": "git.stageAll",
"when": "scmProvider == git && scmResourceGroup == workingTree",
"when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == workingTree",
"group": "1_modification"
},
{
"command": "git.cleanAll",
"when": "scmProvider == git && scmResourceGroup == workingTree",
"when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == workingTree",
"group": "inline"
},
{
"command": "git.stageAll",
"when": "scmProvider == git && scmResourceGroup == workingTree",
"when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == workingTree",
"group": "inline"
}
],
"scm/resource/context": [
{
"command": "git.stage",
"when": "scmProvider == git && scmResourceGroup == merge",
"when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == merge",
"group": "1_modification"
},
{
"command": "git.stage",
"when": "scmProvider == git && scmResourceGroup == merge",
"when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == merge",
"group": "inline"
},
{
"command": "git.openChange",
"when": "scmProvider == git && scmResourceGroup == index",
"when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == index",
"group": "navigation"
},
{
"command": "git.openFile",
"when": "scmProvider == git && scmResourceGroup == index",
"when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == index",
"group": "navigation"
},
{
"command": "git.unstage",
"when": "scmProvider == git && scmResourceGroup == index",
"when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == index",
"group": "1_modification"
},
{
"command": "git.unstage",
"when": "scmProvider == git && scmResourceGroup == index",
"when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == index",
"group": "inline"
},
{
"command": "git.openChange",
"when": "scmProvider == git && scmResourceGroup == workingTree",
"when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == workingTree",
"group": "navigation"
},
{
"command": "git.openFile",
"when": "scmProvider == git && scmResourceGroup == workingTree",
"when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == workingTree",
"group": "navigation"
},
{
"command": "git.stage",
"when": "scmProvider == git && scmResourceGroup == workingTree",
"when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == workingTree",
"group": "1_modification"
},
{
"command": "git.clean",
"when": "scmProvider == git && scmResourceGroup == workingTree",
"when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == workingTree",
"group": "1_modification"
},
{
"command": "git.clean",
"when": "scmProvider == git && scmResourceGroup == workingTree",
"when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == workingTree",
"group": "inline"
},
{
"command": "git.stage",
"when": "scmProvider == git && scmResourceGroup == workingTree",
"when": "scmProvider == git && scmProviderState == idle && scmResourceGroup == workingTree",
"group": "inline"
}
],
+1
View File
@@ -1,4 +1,5 @@
{
"command.init": "Initialize Git Repository",
"command.refresh": "Refresh",
"command.openChange": "Open Change",
"command.openFile": "Open File",
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><path d="M27.459 14.902l-10.439-10.439c-.296-.294-.672-.452-1.089-.452-.417 0-.793.157-1.089.452l-2.248 2.247 2.549 2.549c.249-.112.522-.177.813-.177 1.106 0 2.002.896 2.002 2.002 0 .291-.064.565-.176.814l2.311 2.336c.25-.111.633-.234.923-.234 1.106 0 2 .911 2 2.016s-.894 1.969-2 1.969c-1.105-.001-2.016-.751-2.016-1.985 0-.28.016-.462.119-.704l-2.373-2.374-.023.007v6.274c.747.295 1.277 1.026 1.277 1.875 0 1.105-.878 2.016-1.984 2.016-1.104 0-2.031-.926-2.031-2.031 0-.846.535-1.564 1.28-1.857l.001-6.25c-.762-.282-1.309-1.009-1.309-1.871 0-.28.059-.546.162-.788l-2.555-2.557-7.115 7.114c-.599.601-.601 1.576.001 2.178l10.44 10.518c.296.295.671.45 1.089.45.415 0 .796-.159 1.089-.45l10.391-10.471c.601-.599.599-1.576 0-2.177z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 812 B

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

After

Width:  |  Height:  |  Size: 883 B

+25 -9
View File
@@ -113,13 +113,13 @@ export class CommandCenter {
const right = this.getRightResource(resource);
const title = this.getTitle(resource);
if (!left) {
if (!right) {
// TODO
console.error('oh no');
return;
}
if (!right) {
// TODO
console.error('oh no');
return;
}
if (!left) {
return await commands.executeCommand<void>('vscode.open', right);
}
@@ -130,11 +130,11 @@ export class CommandCenter {
switch (resource.type) {
case Status.INDEX_MODIFIED:
case Status.INDEX_RENAMED:
return resource.uri.with({ scheme: 'git', query: 'HEAD' });
return resource.original.with({ scheme: 'git', query: 'HEAD' });
case Status.MODIFIED:
const uriString = resource.uri.toString();
const [indexStatus] = this.model.indexGroup.resources.filter(r => r.uri.toString() === uriString);
const uriString = resource.original.toString();
const [indexStatus] = this.model.indexGroup.resources.filter(r => r.original.toString() === uriString);
if (indexStatus) {
return resource.uri.with({ scheme: 'git' });
@@ -149,6 +149,8 @@ export class CommandCenter {
case Status.INDEX_MODIFIED:
case Status.INDEX_ADDED:
case Status.INDEX_COPIED:
return resource.uri.with({ scheme: 'git' });
case Status.INDEX_RENAMED:
return resource.uri.with({ scheme: 'git' });
@@ -159,6 +161,15 @@ export class CommandCenter {
case Status.MODIFIED:
case Status.UNTRACKED:
case Status.IGNORED:
const uriString = resource.uri.toString();
const [indexStatus] = this.model.indexGroup.resources.filter(r => r.uri.toString() === uriString);
if (indexStatus && indexStatus.rename) {
return indexStatus.rename;
}
return resource.uri;
case Status.BOTH_MODIFIED:
return resource.uri;
}
@@ -179,6 +190,11 @@ export class CommandCenter {
return '';
}
@command('git.init')
async init(): Promise<void> {
await this.model.init();
}
@command('git.openFile')
async openFile(uri: Uri): Promise<void> {
const scmResource = resolveGitResource(uri);
+2 -3
View File
@@ -6,7 +6,6 @@
'use strict';
import { workspace, window, languages, Disposable, Uri, HoverProvider, Hover, TextEditor, Position, TextDocument, Range, TextEditorDecorationType, WorkspaceEdit } from 'vscode';
import { Model } from './model';
import { filterEvent } from './util';
import * as nls from 'vscode-nls';
@@ -22,7 +21,7 @@ interface Diagnostic {
message: string;
}
// TODO@Joao: hover dissapears if editor is scrolled
// TODO@Joao: hover disappears if editor is scrolled
export class CommitController implements HoverProvider {
private visibleTextEditorsDisposable: Disposable;
@@ -53,7 +52,7 @@ export class CommitController implements HoverProvider {
workspace.applyEdit(edit);
}
constructor(private model: Model) {
constructor() {
this.visibleTextEditorsDisposable = window.onDidChangeVisibleTextEditors(this.onVisibleTextEditors, this);
this.onVisibleTextEditors(window.visibleTextEditors);
+4 -14
View File
@@ -6,8 +6,7 @@
'use strict';
import { workspace, Uri, Disposable, Event, EventEmitter } from 'vscode';
import * as path from 'path';
import { Git } from './git';
import { Model } from './model';
export class GitContentProvider {
@@ -18,7 +17,7 @@ export class GitContentProvider {
private uris = new Set<Uri>();
constructor(private git: Git, private rootPath: string, onGitChange: Event<Uri>) {
constructor(private model: Model, onGitChange: Event<Uri>) {
this.disposables.push(
onGitChange(this.fireChangeEvents, this),
workspace.registerTextDocumentContentProvider('git', this)
@@ -32,19 +31,10 @@ export class GitContentProvider {
}
async provideTextDocumentContent(uri: Uri): Promise<string> {
const treeish = uri.query;
const relativePath = path.relative(this.rootPath, uri.fsPath).replace(/\\/g, '/');
try {
const result = await this.git.exec(this.rootPath, ['show', `${treeish}:${relativePath}`]);
if (result.exitCode !== 0) {
this.uris.delete(uri);
return '';
}
const result = await this.model.show(uri.query, uri);
this.uris.add(uri);
return result.stdout;
return result;
} catch (err) {
this.uris.delete(uri);
return '';
+11 -11
View File
@@ -289,6 +289,11 @@ export class Git {
return new Repository(this, repository, env);
}
async getRepositoryRoot(path: string): Promise<string> {
const result = await this.exec(path, ['rev-parse', '--show-toplevel']);
return result.stdout.trim();
}
async exec(cwd: string, args: string[], options: any = {}): Promise<IExecutionResult> {
options = assign({ cwd }, options || {});
return await this._exec(args, options);
@@ -380,7 +385,7 @@ export class Repository {
constructor(
private _git: Git,
private repository: string,
private repositoryRoot: string,
private env: any = {}
) { }
@@ -388,8 +393,8 @@ export class Repository {
return this._git;
}
get path(): string {
return this.repository;
get root(): string {
return this.repositoryRoot;
}
// TODO@Joao: rename to exec
@@ -397,14 +402,14 @@ export class Repository {
options.env = assign({}, options.env || {});
options.env = assign(options.env, this.env);
return await this.git.exec(this.repository, args, options);
return await this.git.exec(this.repositoryRoot, args, options);
}
stream(args: string[], options: any = {}): cp.ChildProcess {
options.env = assign({}, options.env || {});
options.env = assign(options.env, this.env);
return this.git.stream(this.repository, args, options);
return this.git.stream(this.repositoryRoot, args, options);
}
spawn(args: string[], options: any = {}): cp.ChildProcess {
@@ -709,11 +714,6 @@ export class Repository {
await this.push();
}
async getRoot(): Promise<string> {
const result = await this.run(['rev-parse', '--show-toplevel']);
return result.stdout.trim();
}
async getStatus(): Promise<IFileStatus[]> {
const executionResult = await this.run(['status', '-z', '-u']);
const status = executionResult.stdout;
@@ -861,7 +861,7 @@ export class Repository {
.replace(/^~([^\/]*)\//, (_, user) => `${user ? path.join(path.dirname(homedir), user) : homedir}/`);
if (!path.isAbsolute(templatePath)) {
templatePath = path.join(this.repository, templatePath);
templatePath = path.join(this.repositoryRoot, templatePath);
}
const raw = await readfile(templatePath, 'utf8');
+5 -6
View File
@@ -41,17 +41,15 @@ async function init(disposables: Disposable[]): Promise<void> {
const pathHint = workspace.getConfiguration('git').get<string>('path');
const info = await findGit(pathHint);
const git = new Git({ gitPath: info.path, version: info.version });
const repository = git.open(rootPath);
const repositoryRoot = await repository.getRoot();
const model = new Model(repositoryRoot, repository, onWorkspaceChange);
const model = new Model(git, rootPath, onWorkspaceChange);
outputChannel.appendLine(localize('using git', "Using git {0} from {1}", info.version, info.path));
git.onOutput(str => outputChannel.append(str), null, disposables);
const commitHandler = new CommitController(model);
const commitHandler = new CommitController();
const commandCenter = new CommandCenter(model, outputChannel);
const provider = new GitSCMProvider(model, commandCenter);
const contentProvider = new GitContentProvider(git, rootPath, onGitChange);
const contentProvider = new GitContentProvider(model, onGitChange);
const checkoutStatusBar = new CheckoutStatusBar(model);
const syncStatusBar = new SyncStatusBar(model);
const autoFetcher = new AutoFetcher(model);
@@ -66,7 +64,8 @@ async function init(disposables: Disposable[]): Promise<void> {
checkoutStatusBar,
syncStatusBar,
autoFetcher,
mergeDecorator
mergeDecorator,
model
);
}
+145 -51
View File
@@ -6,8 +6,8 @@
'use strict';
import { Uri, EventEmitter, Event, SCMResource, SCMResourceDecorations, SCMResourceGroup, Disposable, window, workspace } from 'vscode';
import { Repository, Ref, Branch, Remote, PushOptions, Commit } from './git';
import { anyEvent, eventToPromise, filterEvent, mapEvent } from './util';
import { Git, Repository, Ref, Branch, Remote, PushOptions, Commit, GitErrorCodes, GitError } from './git';
import { anyEvent, eventToPromise, filterEvent, mapEvent, EmptyDisposable, combinedDisposable } from './util';
import { memoize, throttle, debounce } from './decorators';
import { watch } from './watch';
import * as path from 'path';
@@ -20,6 +20,12 @@ function getIconUri(iconName: string, theme: string): Uri {
return Uri.file(path.join(iconsRootPath, theme, `${iconName}.svg`));
}
export enum State {
Uninitialized,
Idle,
NotAGitRepository
}
export enum Status {
INDEX_MODIFIED,
INDEX_ADDED,
@@ -43,8 +49,17 @@ export enum Status {
export class Resource implements SCMResource {
get uri(): Uri { return this._uri; }
get uri(): Uri {
if (this.rename && (this._type === Status.MODIFIED || this._type === Status.DELETED || this._type === Status.INDEX_RENAMED)) {
return this.rename;
}
return this._uri;
}
get type(): Status { return this._type; }
get original(): Uri { return this._uri; }
get rename(): Uri | undefined { return this._rename; }
private static Icons = {
light: {
@@ -110,8 +125,8 @@ export class Resource implements SCMResource {
return { strikeThrough: this.strikeThrough, light, dark };
}
constructor(private _uri: Uri, private _type: Status) {
constructor(private _uri: Uri, private _type: Status, private _rename?: Uri) {
// console.log(this);
}
}
@@ -130,7 +145,7 @@ export class MergeGroup extends ResourceGroup {
static readonly ID = 'merge';
constructor(resources: Resource[]) {
constructor(resources: Resource[] = []) {
super(MergeGroup.ID, localize('merge changes', "Merge Changes"), resources);
}
}
@@ -139,7 +154,7 @@ export class IndexGroup extends ResourceGroup {
static readonly ID = 'index';
constructor(resources: Resource[]) {
constructor(resources: Resource[] = []) {
super(IndexGroup.ID, localize('staged changes', "Staged Changes"), resources);
}
}
@@ -148,7 +163,7 @@ export class WorkingTreeGroup extends ResourceGroup {
static readonly ID = 'workingTree';
constructor(resources: Resource[]) {
constructor(resources: Resource[] = []) {
super(WorkingTreeGroup.ID, localize('changes', "Changes"), resources);
}
}
@@ -166,6 +181,8 @@ export enum Operation {
Pull = 1 << 9,
Push = 1 << 10,
Sync = 1 << 11,
Init = 1 << 12,
Show = 1 << 13
}
export interface Operations {
@@ -202,10 +219,18 @@ export interface CommitOptions {
signoff?: boolean;
}
export class Model {
export class Model implements Disposable {
private _onDidChange = new EventEmitter<SCMResourceGroup[]>();
readonly onDidChange: Event<SCMResourceGroup[]> = this._onDidChange.event;
private _onDidChangeState = new EventEmitter<State>();
readonly onDidChangeState: Event<State> = this._onDidChangeState.event;
private _onDidChangeResources = new EventEmitter<SCMResourceGroup[]>();
readonly onDidChangeResources: Event<SCMResourceGroup[]> = this._onDidChangeResources.event;
@memoize
get onDidChange(): Event<void> {
return anyEvent<any>(this.onDidChangeState, this.onDidChangeResources);
}
private _onRunOperation = new EventEmitter<Operation>();
readonly onRunOperation: Event<Operation> = this._onRunOperation.event;
@@ -243,38 +268,6 @@ export class Model {
return result;
}
private _operations = new OperationsImpl();
get operations(): Operations { return this._operations; }
private disposables: Disposable[] = [];
constructor(
private _repositoryRoot: string,
private repository: Repository,
onWorkspaceChange: Event<Uri>
) {
/* We use the native Node `watch` for faster, non debounced events.
* That way we hopefully get the events during the operations we're
* performing, thus sparing useless `git status` calls to refresh
* the model's state.
*/
const gitPath = path.join(_repositoryRoot, '.git');
const { event, disposable } = watch(gitPath);
const onGitChange = mapEvent(event, ({ filename }) => Uri.file(path.join(gitPath, filename)));
const onRelevantGitChange = filterEvent(onGitChange, uri => !/\/\.git\/index\.lock$/.test(uri.fsPath));
onRelevantGitChange(this.onFSChange, this, this.disposables);
this.disposables.push(disposable);
const onNonGitChange = filterEvent(onWorkspaceChange, uri => !/\/\.git\//.test(uri.fsPath));
onNonGitChange(this.onFSChange, this, this.disposables);
this.status();
}
get repositoryRoot(): string {
return this._repositoryRoot;
}
private _HEAD: Branch | undefined;
get HEAD(): Branch | undefined {
return this._HEAD;
@@ -290,6 +283,46 @@ export class Model {
return this._remotes;
}
private _operations = new OperationsImpl();
get operations(): Operations { return this._operations; }
private repository: Repository;
private _state = State.Uninitialized;
get state(): State { return this._state; }
set state(state: State) {
this._state = state;
this._onDidChangeState.fire(state);
this._HEAD = undefined;
this._refs = [];
this._remotes = [];
this._mergeGroup = new MergeGroup();
this._indexGroup = new IndexGroup();
this._workingTreeGroup = new WorkingTreeGroup();
this._onDidChangeResources.fire(this.resources);
}
private repositoryDisposable: Disposable = EmptyDisposable;
constructor(
private git: Git,
private rootPath: string,
private onWorkspaceChange: Event<Uri>
) {
this.status();
}
@throttle
async init(): Promise<void> {
if (this.state !== State.NotAGitRepository) {
return;
}
await this.repository.init();
await this.status();
}
@throttle
async status(): Promise<void> {
await this.run(Operation.Status);
@@ -389,14 +422,40 @@ export class Model {
await this.run(Operation.Sync, () => this.repository.sync());
}
private async run(operation: Operation, fn: () => Promise<void> = () => Promise.resolve()): Promise<void> {
@throttle
async show(ref: string, uri: Uri): Promise<string> {
return await this.run(Operation.Show, async () => {
const relativePath = path.relative(this.repository.root, uri.fsPath).replace(/\\/g, '/');
const result = await this.repository.git.exec(this.repository.root, ['show', `${ref}:${relativePath}`]);
if (result.exitCode !== 0) {
throw new GitError({
message: localize('cantshow', "Could not show object"),
exitCode: result.exitCode
});
}
return result.stdout;
});
}
private async run<T>(operation: Operation, runOperation: () => Promise<T> = () => Promise.resolve<any>(null)): Promise<T> {
return window.withScmProgress(async () => {
this._operations = this._operations.start(operation);
this._onRunOperation.fire(operation);
try {
await fn();
await this.assertIdleState();
const result = await runOperation();
await this.update();
return result;
} catch (err) {
if (err.gitErrorCode === GitErrorCodes.NotAGitRepository) {
this.repositoryDisposable.dispose();
this.state = State.NotAGitRepository;
}
throw err;
} finally {
this._operations = this._operations.end(operation);
this._onDidRunOperation.fire(operation);
@@ -404,6 +463,37 @@ export class Model {
});
}
/* We use the native Node `watch` for faster, non debounced events.
* That way we hopefully get the events during the operations we're
* performing, thus sparing useless `git status` calls to refresh
* the model's state.
*/
private async assertIdleState(): Promise<void> {
if (this.state === State.Idle) {
return;
}
this.repositoryDisposable.dispose();
const disposables: Disposable[] = [];
const repositoryRoot = await this.git.getRepositoryRoot(this.rootPath);
this.repository = this.git.open(repositoryRoot);
const dotGitPath = path.join(repositoryRoot, '.git');
const { event, disposable: watcher } = watch(dotGitPath);
disposables.push(watcher);
const onGitChange = mapEvent(event, ({ filename }) => Uri.file(path.join(dotGitPath, filename)));
const onRelevantGitChange = filterEvent(onGitChange, uri => !/\/\.git\/index\.lock$/.test(uri.fsPath));
onRelevantGitChange(this.onFSChange, this, disposables);
const onNonGitChange = filterEvent(this.onWorkspaceChange, uri => !/\/\.git\//.test(uri.fsPath));
onNonGitChange(this.onFSChange, this, disposables);
this.repositoryDisposable = combinedDisposable(disposables);
this.state = State.Idle;
}
@throttle
private async update(): Promise<void> {
const status = await this.repository.getStatus();
@@ -434,7 +524,8 @@ export class Model {
const merge: Resource[] = [];
status.forEach(raw => {
const uri = Uri.file(path.join(this.repositoryRoot, raw.path));
const uri = Uri.file(path.join(this.repository.root, raw.path));
const renameUri = raw.rename ? Uri.file(path.join(this.repository.root, raw.rename)) : undefined;
switch (raw.x + raw.y) {
case '??': return workingTree.push(new Resource(uri, Status.UNTRACKED));
@@ -454,21 +545,20 @@ export class Model {
case 'M': index.push(new Resource(uri, Status.INDEX_MODIFIED)); isModifiedInIndex = true; break;
case 'A': index.push(new Resource(uri, Status.INDEX_ADDED)); break;
case 'D': index.push(new Resource(uri, Status.INDEX_DELETED)); break;
case 'R': index.push(new Resource(uri, Status.INDEX_RENAMED/*, raw.rename*/)); break;
case 'R': index.push(new Resource(uri, Status.INDEX_RENAMED, renameUri)); break;
case 'C': index.push(new Resource(uri, Status.INDEX_COPIED)); break;
}
switch (raw.y) {
case 'M': workingTree.push(new Resource(uri, Status.MODIFIED/*, raw.rename*/)); break;
case 'D': workingTree.push(new Resource(uri, Status.DELETED/*, raw.rename*/)); break;
case 'M': workingTree.push(new Resource(uri, Status.MODIFIED, renameUri)); break;
case 'D': workingTree.push(new Resource(uri, Status.DELETED, renameUri)); break;
}
});
this._mergeGroup = new MergeGroup(merge);
this._indexGroup = new IndexGroup(index);
this._workingTreeGroup = new WorkingTreeGroup(workingTree);
this._onDidChange.fire(this.resources);
this._onDidChangeResources.fire(this.resources);
}
private onFSChange(uri: Uri): void {
@@ -503,4 +593,8 @@ export class Model {
await eventToPromise(this.onDidRunOperation);
}
}
dispose(): void {
this.repositoryDisposable.dispose();
}
}
+16 -2
View File
@@ -6,17 +6,31 @@
'use strict';
import { scm, Uri, Disposable, SCMProvider, SCMResourceGroup, Event, ProviderResult, workspace } from 'vscode';
import { Model, Resource, ResourceGroup } from './model';
import { Model, Resource, ResourceGroup, State } from './model';
import { CommandCenter } from './commands';
import { mapEvent } from './util';
export class GitSCMProvider implements SCMProvider {
private disposables: Disposable[] = [];
get resources(): SCMResourceGroup[] { return this.model.resources; }
get onDidChange(): Event<SCMResourceGroup[]> { return this.model.onDidChange; }
get onDidChange(): Event<SCMResourceGroup[]> {
return mapEvent(this.model.onDidChange, () => this.model.resources);
}
get label(): string { return 'Git'; }
get state(): string {
switch (this.model.state) {
case State.Uninitialized: return 'uninitialized';
case State.Idle: return 'idle';
case State.NotAGitRepository: return 'norepo';
default: return '';
}
}
get count(): number {
const countBadge = workspace.getConfiguration('git').get<string>('countBadge');
+2 -3
View File
@@ -30,9 +30,7 @@ export class CheckoutStatusBar {
const HEAD = this.model.HEAD;
if (!HEAD) {
this.raw.command = '';
this.raw.color = 'rgb(100, 100, 100)';
this.raw.text = 'unknown';
this.raw.hide();
return;
}
@@ -48,6 +46,7 @@ export class CheckoutStatusBar {
(this.model.workingTreeGroup.resources.length > 0 ? '*' : '') +
(this.model.indexGroup.resources.length > 0 ? '+' : '') +
(this.model.mergeGroup.resources.length > 0 ? '!' : '');
this.raw.show();
}
dispose(): void {
+2
View File
@@ -28,6 +28,8 @@ export function combinedDisposable(disposables: IDisposable[]): IDisposable {
return toDisposable(() => dispose(disposables));
}
export const EmptyDisposable = toDisposable(() => null);
export function mapEvent<I, O>(event: Event<I>, map: (i: I) => O): Event<O> {
return (listener, thisArgs = null, disposables?) => event(i => listener.call(thisArgs, map(i)), null, disposables);
}
+1 -1
View File
@@ -57,7 +57,7 @@ export function activate(context: ExtensionContext) {
};
// Create the language client and start the client.
let client = new LanguageClient('html', localize('htmlserver.name', 'HTML Language Server'), serverOptions, clientOptions, true);
let client = new LanguageClient('html', localize('htmlserver.name', 'HTML Language Server'), serverOptions, clientOptions);
let disposable = client.start();
context.subscriptions.push(disposable);
client.onReady().then(() => {
+1 -4
View File
@@ -17,7 +17,6 @@ import URI from './utils/uri';
import * as URL from 'url';
import Strings = require('./utils/strings');
import { JSONDocument, JSONSchema, LanguageSettings, getLanguageService } from 'vscode-json-languageservice';
import { ProjectJSONContribution } from './jsoncontributions/projectJSONContribution';
import { getLanguageModelCache } from './languageModelCache';
import * as nls from 'vscode-nls';
@@ -119,9 +118,7 @@ let schemaRequestService = (uri: string): Thenable<string> => {
let languageService = getLanguageService({
schemaRequestService,
workspaceContext,
contributions: [
new ProjectJSONContribution()
]
contributions: []
});
// The settings interface describes the server relevant settings part
@@ -1,281 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { MarkedString, CompletionItemKind, CompletionItem, InsertTextFormat } from 'vscode-languageserver';
import Strings = require('../utils/strings');
import { XHRResponse, getErrorStatusDescription, xhr } from 'request-light';
import { JSONWorkerContribution, JSONPath, CompletionsCollector } from 'vscode-json-languageservice';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
const FEED_INDEX_URL = 'https://api.nuget.org/v3/index.json';
const LIMIT = 30;
const RESOLVE_ID = 'ProjectJSONContribution-';
const CACHE_EXPIRY = 1000 * 60 * 5; // 5 minutes
interface NugetServices {
'SearchQueryService'?: string;
'SearchAutocompleteService'?: string;
'PackageBaseAddress/3.0.0'?: string;
[key: string]: string;
}
export class ProjectJSONContribution implements JSONWorkerContribution {
private cachedProjects: { [id: string]: { version: string, description: string, time: number } } = {};
private cacheSize: number = 0;
private nugetIndexPromise: Thenable<NugetServices>;
public constructor() {
}
private isProjectJSONFile(resource: string): boolean {
return Strings.endsWith(resource, '/project.json');
}
private completeWithCache(id: string, item: CompletionItem): boolean {
let entry = this.cachedProjects[id];
if (entry) {
if (new Date().getTime() - entry.time > CACHE_EXPIRY) {
delete this.cachedProjects[id];
this.cacheSize--;
return false;
}
let insertTextValue = item.insertText;
item.detail = entry.version;
item.documentation = entry.description;
item.insertText = insertTextValue.replace(/\$1/, '${1:' + entry.version + '}');
return true;
}
return false;
}
private addCached(id: string, version: string, description: string) {
this.cachedProjects[id] = { version, description, time: new Date().getTime() };
this.cacheSize++;
if (this.cacheSize > 50) {
let currentTime = new Date().getTime();
for (let id in this.cachedProjects) {
let entry = this.cachedProjects[id];
if (currentTime - entry.time > CACHE_EXPIRY) {
delete this.cachedProjects[id];
this.cacheSize--;
}
}
}
}
private getNugetIndex(): Thenable<NugetServices> {
if (!this.nugetIndexPromise) {
this.nugetIndexPromise = this.makeJSONRequest<any>(FEED_INDEX_URL).then(indexContent => {
let services: NugetServices = {};
if (indexContent && Array.isArray(indexContent.resources)) {
let resources = <any[]>indexContent.resources;
for (let i = resources.length - 1; i >= 0; i--) {
let type = resources[i]['@type'];
let id = resources[i]['@id'];
if (type && id) {
services[type] = id;
}
}
}
return services;
});
}
return this.nugetIndexPromise;
}
private getNugetService(serviceType: string): Thenable<string> {
return this.getNugetIndex().then(services => {
let serviceURL = services[serviceType];
if (!serviceURL) {
return Promise.reject<string>(localize('json.nugget.error.missingservice', 'NuGet index document is missing service {0}', serviceType));
}
return serviceURL;
});
}
public collectDefaultCompletions(resource: string, result: CompletionsCollector): Thenable<any> {
if (this.isProjectJSONFile(resource)) {
let insertText = JSON.stringify({
'version': '${1:1.0.0-*}',
'dependencies': {},
'frameworks': {
'net461': {},
'netcoreapp1.0': {}
}
}, null, '\t');
result.add({ kind: CompletionItemKind.Class, label: localize('json.project.default', 'Default project.json'), insertText, insertTextFormat: InsertTextFormat.Snippet, documentation: '' });
}
return null;
}
private makeJSONRequest<T>(url: string): Thenable<T> {
return xhr({
url: url
}).then(success => {
if (success.status === 200) {
try {
return <T>JSON.parse(success.responseText);
} catch (e) {
return Promise.reject<T>(localize('json.nugget.error.invalidformat', '{0} is not a valid JSON document', url));
}
}
return Promise.reject<T>(localize('json.nugget.error.indexaccess', 'Request to {0} failed: {1}', url, success.responseText));
}, (error: XHRResponse) => {
return Promise.reject<T>(localize('json.nugget.error.access', 'Request to {0} failed: {1}', url, getErrorStatusDescription(error.status)));
});
}
public collectPropertyCompletions(resource: string, location: JSONPath, currentWord: string, addValue: boolean, isLast: boolean, result: CompletionsCollector): Thenable<any> {
if (this.isProjectJSONFile(resource) && (matches(location, ['dependencies']) || matches(location, ['frameworks', '*', 'dependencies']) || matches(location, ['frameworks', '*', 'frameworkAssemblies']))) {
return this.getNugetService('SearchAutocompleteService').then(service => {
let queryUrl: string;
if (currentWord.length > 0) {
queryUrl = service + '?q=' + encodeURIComponent(currentWord) + '&take=' + LIMIT;
} else {
queryUrl = service + '?take=' + LIMIT;
}
return this.makeJSONRequest<any>(queryUrl).then(resultObj => {
if (Array.isArray(resultObj.data)) {
let results = <any[]>resultObj.data;
for (let i = 0; i < results.length; i++) {
let name = results[i];
let insertText = JSON.stringify(name);
if (addValue) {
insertText += ': "$1"';
if (!isLast) {
insertText += ',';
}
}
let item: CompletionItem = { kind: CompletionItemKind.Property, label: name, insertText: insertText, insertTextFormat: InsertTextFormat.Snippet, filterText: JSON.stringify(name) };
if (!this.completeWithCache(name, item)) {
item.data = RESOLVE_ID + name;
}
result.add(item);
}
if (results.length === LIMIT) {
result.setAsIncomplete();
}
}
}, error => {
result.error(error);
});
}, error => {
result.error(error);
});
};
return null;
}
public collectValueCompletions(resource: string, location: JSONPath, currentKey: string, result: CompletionsCollector): Thenable<any> {
if (this.isProjectJSONFile(resource) && (matches(location, ['dependencies']) || matches(location, ['frameworks', '*', 'dependencies']) || matches(location, ['frameworks', '*', 'frameworkAssemblies']))) {
return this.getNugetService('PackageBaseAddress/3.0.0').then(service => {
let queryUrl = service + currentKey + '/index.json';
return this.makeJSONRequest<any>(queryUrl).then(obj => {
if (Array.isArray(obj.versions)) {
let results = <any[]>obj.versions;
for (let i = 0; i < results.length; i++) {
let curr = results[i];
let name = JSON.stringify(curr);
let label = name;
let documentation = '';
result.add({ kind: CompletionItemKind.Class, label: label, insertText: name, documentation: documentation });
}
if (results.length === LIMIT) {
result.setAsIncomplete();
}
}
}, error => {
result.error(error);
});
}, error => {
result.error(error);
});
}
return null;
}
public getInfoContribution(resource: string, location: JSONPath): Thenable<MarkedString[]> {
if (this.isProjectJSONFile(resource) && (matches(location, ['dependencies', '*']) || matches(location, ['frameworks', '*', 'dependencies', '*']) || matches(location, ['frameworks', '*', 'frameworkAssemblies', '*']))) {
let pack = <string>location[location.length - 1];
return this.getNugetService('SearchQueryService').then(service => {
let queryUrl = service + '?q=' + encodeURIComponent(pack) + '&take=' + 5;
return this.makeJSONRequest<any>(queryUrl).then(resultObj => {
let htmlContent: MarkedString[] = [];
htmlContent.push(localize('json.nugget.package.hover', '{0}', pack));
if (Array.isArray(resultObj.data)) {
let results = <any[]>resultObj.data;
for (let i = 0; i < results.length; i++) {
let res = results[i];
this.addCached(res.id, res.version, res.description);
if (res.id === pack) {
if (res.description) {
htmlContent.push(MarkedString.fromPlainText(res.description));
}
if (res.version) {
htmlContent.push(MarkedString.fromPlainText(localize('json.nugget.version.hover', 'Latest version: {0}', res.version)));
}
break;
}
}
}
return htmlContent;
}, (error) => {
return null;
});
}, (error) => {
return null;
});
}
return null;
}
public resolveSuggestion(item: CompletionItem): Thenable<CompletionItem> {
if (item.data && Strings.startsWith(item.data, RESOLVE_ID)) {
let pack = item.data.substring(RESOLVE_ID.length);
if (this.completeWithCache(pack, item)) {
return Promise.resolve(item);
}
return this.getNugetService('SearchQueryService').then(service => {
let queryUrl = service + '?q=' + encodeURIComponent(pack) + '&take=' + 10;
return this.makeJSONRequest<CompletionItem>(queryUrl).then(resultObj => {
let itemResolved = false;
if (Array.isArray(resultObj.data)) {
let results = <any[]>resultObj.data;
for (let i = 0; i < results.length; i++) {
let curr = results[i];
this.addCached(curr.id, curr.version, curr.description);
if (curr.id === pack) {
this.completeWithCache(pack, item);
itemResolved = true;
}
}
}
return itemResolved ? item : null;
});
});
};
return null;
}
}
function matches(segments: JSONPath, pattern: string[]) {
let k = 0;
for (let i = 0; k < pattern.length && i < segments.length; i++) {
if (pattern[k] === segments[i] || pattern[k] === '*') {
k++;
} else if (pattern[k] !== '**') {
return false;
}
}
return k === pattern.length;
}
+1 -1
View File
@@ -1,5 +1,5 @@
{
"markdown.preview.doubleClickToSwitchToEditor.desc": "Enable double clicking in the markdown preview switches to the editor.",
"markdown.preview.doubleClickToSwitchToEditor.desc": "Double click in the markdown preview to switch to the editor.",
"markdown.preview.fontFamily.desc": "Controls the font family used in the markdown preview.",
"markdown.preview.fontSize.desc": "Controls the font size in pixels used in the markdown preview.",
"markdown.preview.lineHeight.desc": "Controls the line height used in the markdown preview. This number is relative to the font size.",
@@ -8,14 +8,14 @@
import * as vscode from 'vscode';
import { MarkdownEngine } from './markdownEngine';
import { TableOfContentProvider } from './tableOfContentsProvider';
import { TableOfContentsProvider } from './tableOfContentsProvider';
export default class MDDocumentSymbolProvider implements vscode.DocumentSymbolProvider {
constructor(private engine: MarkdownEngine) { }
provideDocumentSymbols(document: vscode.TextDocument): vscode.ProviderResult<vscode.SymbolInformation[]> {
const toc = new TableOfContentProvider(this.engine, document);
const toc = new TableOfContentsProvider(this.engine, document);
return toc.getToc().map(entry => {
return new vscode.SymbolInformation(entry.text, vscode.SymbolKind.Module, '', entry.location);
});
+2 -2
View File
@@ -12,7 +12,7 @@ import { MarkdownEngine } from './markdownEngine';
import DocumentLinkProvider from './documentLinkProvider';
import MDDocumentSymbolProvider from './documentSymbolProvider';
import { MDDocumentContentProvider, getMarkdownUri, isMarkdownFile } from './previewContentProvider';
import { TableOfContentProvider } from './tableOfContentsProvider';
import { TableOfContentsProvider } from './tableOfContentsProvider';
interface IPackageInfo {
name: string;
@@ -78,7 +78,7 @@ export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.commands.registerCommand('_markdown.openDocumentLink', (args: OpenDocumentLinkArgs) => {
const tryRevealLine = (editor: vscode.TextEditor) => {
if (editor && args.fragment) {
const toc = new TableOfContentProvider(engine, editor.document);
const toc = new TableOfContentsProvider(engine, editor.document);
const line = toc.lookup(args.fragment);
if (!isNaN(line)) {
return editor.revealRange(
+4 -1
View File
@@ -7,6 +7,7 @@
import * as vscode from 'vscode';
import * as path from 'path';
import { TableOfContentsProvider } from './tableOfContentsProvider';
export interface IToken {
type: string;
@@ -44,7 +45,9 @@ export class MarkdownEngine {
}
return `<pre class="hljs"><code><div>${this.engine.utils.escapeHtml(str)}</div></code></pre>`;
}
}).use(mdnh, {});
}).use(mdnh, {
slugify: (header: string) => TableOfContentsProvider.slugify(header)
});
for (const renderName of ['paragraph_open', 'heading_open', 'image', 'code_block', 'blockquote_open', 'list_item_open']) {
this.addLineNumberRenderer(this.md, renderName);
@@ -20,14 +20,12 @@ export function getMarkdownUri(uri: vscode.Uri) {
export class MDDocumentContentProvider implements vscode.TextDocumentContentProvider {
private _onDidChange = new vscode.EventEmitter<vscode.Uri>();
private _waiting: boolean;
private _waiting: boolean = false;
constructor(
private engine: MarkdownEngine,
private context: vscode.ExtensionContext
) {
this._waiting = false;
}
) { }
private getMediaPath(mediaFile: string): string {
return vscode.Uri.file(this.context.asAbsolutePath(path.join('media', mediaFile))).toString();
@@ -38,27 +36,28 @@ export class MDDocumentContentProvider implements vscode.TextDocumentContentProv
}
private fixHref(resource: vscode.Uri, href: string): string {
if (href) {
// Use href if it is already an URL
if (vscode.Uri.parse(href).scheme) {
return href;
}
// Use href as file URI if it is absolute
if (this.isAbsolute(href)) {
return vscode.Uri.file(href).toString();
}
// use a workspace relative path if there is a workspace
let rootPath = vscode.workspace.rootPath;
if (rootPath) {
return vscode.Uri.file(path.join(rootPath, href)).toString();
}
// otherwise look relative to the markdown file
return vscode.Uri.file(path.join(path.dirname(resource.fsPath), href)).toString();
if (!href) {
return href;
}
return href;
// Use href if it is already an URL
if (vscode.Uri.parse(href).scheme) {
return href;
}
// Use href as file URI if it is absolute
if (this.isAbsolute(href)) {
return vscode.Uri.file(href).toString();
}
// use a workspace relative path if there is a workspace
let rootPath = vscode.workspace.rootPath;
if (rootPath) {
return vscode.Uri.file(path.join(rootPath, href)).toString();
}
// otherwise look relative to the markdown file
return vscode.Uri.file(path.join(path.dirname(resource.fsPath), href)).toString();
}
private computeCustomStyleSheetIncludes(uri: vscode.Uri): string {
@@ -16,7 +16,7 @@ export interface TocEntry {
location: vscode.Location;
}
export class TableOfContentProvider {
export class TableOfContentsProvider {
private toc: TocEntry[];
public constructor(
@@ -35,7 +35,7 @@ export class TableOfContentProvider {
}
public lookup(fragment: string): number {
const slug = TableOfContentProvider.slugify(fragment);
const slug = TableOfContentsProvider.slugify(fragment);
for (const entry of this.getToc()) {
if (entry.slug === slug) {
return entry.line;
@@ -51,11 +51,11 @@ export class TableOfContentProvider {
for (const heading of tokens.filter(token => token.type === 'heading_open')) {
const lineNumber = heading.map[0];
const line = document.lineAt(lineNumber);
const href = TableOfContentProvider.slugify(line.text);
const href = TableOfContentsProvider.slugify(line.text);
if (href) {
toc.push({
slug: href,
text: TableOfContentProvider.getHeaderText(line.text),
text: TableOfContentsProvider.getHeaderText(line.text),
line: lineNumber,
location: new vscode.Location(document.uri, line.range)
});
@@ -69,9 +69,12 @@ export class TableOfContentProvider {
}
public static slugify(header: string): string {
return encodeURI(TableOfContentProvider.getHeaderText(header)
return encodeURI(header.trim()
.toLowerCase()
.replace(/\s/g, '-'));
.replace(/[\]\[\!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~]/g, '')
.replace(/\s+/g, '-')
.replace(/^\-+/, '')
.replace(/\-+$/, ''));
}
}
+1
View File
@@ -4,6 +4,7 @@
"version": "0.0.0",
"license": "TextMate Bundle License",
"repositoryURL": "https://github.com/textmate/perl.tmbundle",
"description": "The files syntaxes/perl.tmLanguage.json and syntaxes/perl6.tmLanguage.json were derived from Perl.plist and Perl 6.tmLanguage from https://github.com/textmate/perl.tmbundle.",
"licenseDetail": [
"Copyright (c) textmate-perl.tmbundle project authors",
"",
+5 -2
View File
@@ -3,6 +3,9 @@
"version": "0.1.0",
"publisher": "vscode",
"engines": { "vscode": "*" },
"scripts": {
"update-grammar": "node ../../build/npm/update-grammar.js textmate/perl.tmbundle Syntaxes/Perl.plist ./syntaxes/perl.tmLanguage.json Syntaxes/Perl%206.tmLanguage ./syntaxes/perl6.tmLanguage.json"
},
"contributes": {
"languages": [{
"id": "perl",
@@ -20,11 +23,11 @@
"grammars": [{
"language": "perl",
"scopeName": "source.perl",
"path": "./syntaxes/Perl.plist"
"path": "./syntaxes/perl.tmLanguage.json"
}, {
"language": "perl6",
"scopeName": "source.perl.6",
"path": "./syntaxes/Perl 6.tmLanguage"
"path": "./syntaxes/perl6.tmLanguage.json"
}]
}
}
-490
View File
@@ -1,490 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>fileTypes</key>
<array>
<string>p6</string>
<string>pl6</string>
<string>pm6</string>
<string>nqp</string>
</array>
<key>firstLineMatch</key>
<string>(^#!.*\b(perl6|nqp)\b)|use\s+v6</string>
<key>keyEquivalent</key>
<string>^~P</string>
<key>name</key>
<string>Perl 6</string>
<key>patterns</key>
<array>
<dict>
<key>begin</key>
<string>^=begin</string>
<key>end</key>
<string>^=end</string>
<key>name</key>
<string>comment.block.perl</string>
</dict>
<dict>
<key>begin</key>
<string>(^[ \t]+)?(?=#)</string>
<key>beginCaptures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>punctuation.whitespace.comment.leading.perl</string>
</dict>
</dict>
<key>end</key>
<string>(?!\G)</string>
<key>patterns</key>
<array>
<dict>
<key>begin</key>
<string>#</string>
<key>beginCaptures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.comment.perl</string>
</dict>
</dict>
<key>end</key>
<string>\n</string>
<key>name</key>
<string>comment.line.number-sign.perl</string>
</dict>
</array>
</dict>
<dict>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>storage.type.class.perl.6</string>
</dict>
<key>3</key>
<dict>
<key>name</key>
<string>entity.name.type.class.perl.6</string>
</dict>
</dict>
<key>match</key>
<string>(class|enum|grammar|knowhow|module|package|role|slang|subset)(\s+)(((?:::|')?(?:([a-zA-Z_\x{C0}-\x{FF}\$])([a-zA-Z0-9_\x{C0}-\x{FF}\\$]|[\-'][a-zA-Z0-9_\x{C0}-\x{FF}\$])*))+)</string>
<key>name</key>
<string>meta.class.perl.6</string>
</dict>
<dict>
<key>begin</key>
<string>(?&lt;=\s)'</string>
<key>beginCaptures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.begin.perl</string>
</dict>
</dict>
<key>end</key>
<string>'</string>
<key>endCaptures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.end.perl</string>
</dict>
</dict>
<key>name</key>
<string>string.quoted.single.perl</string>
<key>patterns</key>
<array>
<dict>
<key>match</key>
<string>\\['\\]</string>
<key>name</key>
<string>constant.character.escape.perl</string>
</dict>
</array>
</dict>
<dict>
<key>begin</key>
<string>"</string>
<key>beginCaptures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.begin.perl</string>
</dict>
</dict>
<key>end</key>
<string>"</string>
<key>endCaptures</key>
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>punctuation.definition.string.end.perl</string>
</dict>
</dict>
<key>name</key>
<string>string.quoted.double.perl</string>
<key>patterns</key>
<array>
<dict>
<key>match</key>
<string>\\[abtnfre"\\]</string>
<key>name</key>
<string>constant.character.escape.perl</string>
</dict>
</array>
</dict>
<dict>
<key>begin</key>
<string>q(q|to|heredoc)*\s*:?(q|to|heredoc)*\s*/(.+)/</string>
<key>end</key>
<string>\3</string>
<key>name</key>
<string>string.quoted.single.heredoc.perl</string>
</dict>
<dict>
<key>begin</key>
<string>(q|Q)(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\s*:?(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\s*{{</string>
<key>end</key>
<string>}}</string>
<key>name</key>
<string>string.quoted.double.heredoc.brace.perl</string>
<key>patterns</key>
<array>
<dict>
<key>include</key>
<string>#qq_brace_string_content</string>
</dict>
</array>
</dict>
<dict>
<key>begin</key>
<string>(q|Q)(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\s*:?(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\s*\(\(</string>
<key>end</key>
<string>\)\)</string>
<key>name</key>
<string>string.quoted.double.heredoc.paren.perl</string>
<key>patterns</key>
<array>
<dict>
<key>include</key>
<string>#qq_paren_string_content</string>
</dict>
</array>
</dict>
<dict>
<key>begin</key>
<string>(q|Q)(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\s*:?(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\s*\[\[</string>
<key>end</key>
<string>\]\]</string>
<key>name</key>
<string>string.quoted.double.heredoc.bracket.perl</string>
<key>patterns</key>
<array>
<dict>
<key>include</key>
<string>#qq_bracket_string_content</string>
</dict>
</array>
</dict>
<dict>
<key>begin</key>
<string>(q|Q)(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\s*:?(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\s*{</string>
<key>end</key>
<string>}</string>
<key>name</key>
<string>string.quoted.single.heredoc.brace.perl</string>
<key>patterns</key>
<array>
<dict>
<key>include</key>
<string>#qq_brace_string_content</string>
</dict>
</array>
</dict>
<dict>
<key>begin</key>
<string>(q|Q)(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\s*:?(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\s*/</string>
<key>end</key>
<string>/</string>
<key>name</key>
<string>string.quoted.single.heredoc.slash.perl</string>
<key>patterns</key>
<array>
<dict>
<key>include</key>
<string>#qq_slash_string_content</string>
</dict>
</array>
</dict>
<dict>
<key>begin</key>
<string>(q|Q)(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\s*:?(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\s*\(</string>
<key>end</key>
<string>\)</string>
<key>name</key>
<string>string.quoted.single.heredoc.paren.perl</string>
<key>patterns</key>
<array>
<dict>
<key>include</key>
<string>#qq_paren_string_content</string>
</dict>
</array>
</dict>
<dict>
<key>begin</key>
<string>(q|Q)(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\s*:?(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\s*\[</string>
<key>end</key>
<string>\]</string>
<key>name</key>
<string>string.quoted.single.heredoc.bracket.perl</string>
<key>patterns</key>
<array>
<dict>
<key>include</key>
<string>#qq_bracket_string_content</string>
</dict>
</array>
</dict>
<dict>
<key>begin</key>
<string>(q|Q)(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\s*:?(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\s*'</string>
<key>end</key>
<string>'</string>
<key>name</key>
<string>string.quoted.single.heredoc.single.perl</string>
<key>patterns</key>
<array>
<dict>
<key>include</key>
<string>#qq_single_string_content</string>
</dict>
</array>
</dict>
<dict>
<key>begin</key>
<string>(q|Q)(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\s*:?(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\s*"</string>
<key>end</key>
<string>"</string>
<key>name</key>
<string>string.quoted.single.heredoc.double.perl</string>
<key>patterns</key>
<array>
<dict>
<key>include</key>
<string>#qq_double_string_content</string>
</dict>
</array>
</dict>
<dict>
<key>match</key>
<string>\b\$\w+\b</string>
<key>name</key>
<string>variable.other.perl</string>
</dict>
<dict>
<key>match</key>
<string>\b(macro|sub|submethod|method|multi|proto|only|rule|token|regex|category)\b</string>
<key>name</key>
<string>storage.type.declare.routine.perl</string>
</dict>
<dict>
<key>match</key>
<string>\b(self)\b</string>
<key>name</key>
<string>variable.language.perl</string>
</dict>
<dict>
<key>match</key>
<string>\b(use|require)\b</string>
<key>name</key>
<string>keyword.other.include.perl</string>
</dict>
<dict>
<key>match</key>
<string>\b(if|else|elsif|unless)\b</string>
<key>name</key>
<string>keyword.control.conditional.perl</string>
</dict>
<dict>
<key>match</key>
<string>\b(let|my|our|state|temp|has|constant)\b</string>
<key>name</key>
<string>storage.type.variable.perl</string>
</dict>
<dict>
<key>match</key>
<string>\b(for|loop|repeat|while|until|gather|given)\b</string>
<key>name</key>
<string>keyword.control.repeat.perl</string>
</dict>
<dict>
<key>match</key>
<string>\b(take|do|when|next|last|redo|return|contend|maybe|defer|default|exit|make|continue|break|goto|leave|async|lift)\b</string>
<key>name</key>
<string>keyword.control.flowcontrol.perl</string>
</dict>
<dict>
<key>match</key>
<string>\b(is|as|but|trusts|of|returns|handles|where|augment|supersede)\b</string>
<key>name</key>
<string>storage.modifier.type.constraints.perl</string>
</dict>
<dict>
<key>match</key>
<string>\b(BEGIN|CHECK|INIT|START|FIRST|ENTER|LEAVE|KEEP|UNDO|NEXT|LAST|PRE|POST|END|CATCH|CONTROL|TEMP)\b</string>
<key>name</key>
<string>meta.function.perl</string>
</dict>
<dict>
<key>match</key>
<string>\b(die|fail|try|warn)\b</string>
<key>name</key>
<string>keyword.control.control-handlers.perl</string>
</dict>
<dict>
<key>match</key>
<string>\b(prec|irs|ofs|ors|export|deep|binary|unary|reparsed|rw|parsed|cached|readonly|defequiv|will|ref|copy|inline|tighter|looser|equiv|assoc|required)\b</string>
<key>name</key>
<string>storage.modifier.perl</string>
</dict>
<dict>
<key>match</key>
<string>\b(NaN|Inf)\b</string>
<key>name</key>
<string>constant.numeric.perl</string>
</dict>
<dict>
<key>match</key>
<string>\b(oo|fatal)\b</string>
<key>name</key>
<string>keyword.other.pragma.perl</string>
</dict>
<dict>
<key>match</key>
<string>\b(Object|Any|Junction|Whatever|Capture|MatchSignature|Proxy|Matcher|Package|Module|ClassGrammar|Scalar|Array|Hash|KeyHash|KeySet|KeyBagPair|List|Seq|Range|Set|Bag|Mapping|Void|UndefFailure|Exception|Code|Block|Routine|Sub|MacroMethod|Submethod|Regex|Str|str|Blob|Char|ByteCodepoint|Grapheme|StrPos|StrLen|Version|NumComplex|num|complex|Bit|bit|bool|True|FalseIncreasing|Decreasing|Ordered|Callable|AnyCharPositional|Associative|Ordering|KeyExtractorComparator|OrderingPair|IO|KitchenSink|RoleInt|int|int1|int2|int4|int8|int16|int32|int64Rat|rat|rat1|rat2|rat4|rat8|rat16|rat32|rat64Buf|buf|buf1|buf2|buf4|buf8|buf16|buf32|buf64UInt|uint|uint1|uint2|uint4|uint8|uint16|uint32uint64|Abstraction|utf8|utf16|utf32)\b</string>
<key>name</key>
<string>support.type.perl6</string>
</dict>
<dict>
<key>match</key>
<string>\b(div|xx|x|mod|also|leg|cmp|before|after|eq|ne|le|lt|not|gt|ge|eqv|ff|fff|and|andthen|or|xor|orelse|extra|lcm|gcd)\b</string>
<key>name</key>
<string>keyword.operator.perl</string>
</dict>
<dict>
<key>match</key>
<string>(\$|@|%|&amp;)(\*|:|!|\^|~|=|\?|(&lt;(?=.+&gt;)))?([a-zA-Z_\x{C0}-\x{FF}\$])([a-zA-Z0-9_\x{C0}-\x{FF}\$]|[\-'][a-zA-Z0-9_\x{C0}-\x{FF}\$])*</string>
<key>name</key>
<string>variable.other.identifier.perl.6</string>
</dict>
<dict>
<key>match</key>
<string>\b(eager|hyper|substr|index|rindex|grep|map|sort|join|lines|hints|chmod|split|reduce|min|max|reverse|truncate|zip|cat|roundrobin|classify|first|sum|keys|values|pairs|defined|delete|exists|elems|end|kv|any|all|one|wrap|shape|key|value|name|pop|push|shift|splice|unshift|floor|ceiling|abs|exp|log|log10|rand|sign|sqrt|sin|cos|tan|round|strand|roots|cis|unpolar|polar|atan2|pick|chop|p5chop|chomp|p5chomp|lc|lcfirst|uc|ucfirst|capitalize|normalize|pack|unpack|quotemeta|comb|samecase|sameaccent|chars|nfd|nfc|nfkd|nfkc|printf|sprintf|caller|evalfile|run|runinstead|nothing|want|bless|chr|ord|gmtime|time|eof|localtime|gethost|getpw|chroot|getlogin|getpeername|kill|fork|wait|perl|graphs|codes|bytes|clone|print|open|read|write|readline|say|seek|close|opendir|readdir|slurp|spurt|shell|run|pos|fmt|vec|link|unlink|symlink|uniq|pair|asin|atan|sec|cosec|cotan|asec|acosec|acotan|sinh|cosh|tanh|asinh|done|acos|acosh|atanh|sech|cosech|cotanh|sech|acosech|acotanh|asech|ok|nok|plan_ok|dies_ok|lives_ok|skip|todo|pass|flunk|force_todo|use_ok|isa_ok|diag|is_deeply|isnt|like|skip_rest|unlike|cmp_ok|eval_dies_ok|nok_error|eval_lives_ok|approx|is_approx|throws_ok|version_lt|plan|EVAL|succ|pred|times|nonce|once|signature|new|connect|operator|undef|undefine|sleep|from|to|infix|postfix|prefix|circumfix|postcircumfix|minmax|lazy|count|unwrap|getc|pi|e|context|void|quasi|body|each|contains|rewinddir|subst|can|isa|flush|arity|assuming|rewind|callwith|callsame|nextwith|nextsame|attr|eval_elsewhere|none|srand|trim|trim_start|trim_end|lastcall|WHAT|WHERE|HOW|WHICH|VAR|WHO|WHENCE|ACCEPTS|REJECTS|not|true|iterator|by|re|im|invert|flip|gist|flat|tree|is-prime|throws_like|trans)\b</string>
<key>name</key>
<string>support.function.perl</string>
</dict>
</array>
<key>repository</key>
<dict>
<key>qq_brace_string_content</key>
<dict>
<key>begin</key>
<string>{</string>
<key>end</key>
<string>}</string>
<key>patterns</key>
<array>
<dict>
<key>include</key>
<string>#qq_brace_string_content</string>
</dict>
</array>
</dict>
<key>qq_bracket_string_content</key>
<dict>
<key>begin</key>
<string>\[</string>
<key>end</key>
<string>\]</string>
<key>patterns</key>
<array>
<dict>
<key>include</key>
<string>#qq_bracket_string_content</string>
</dict>
</array>
</dict>
<key>qq_double_string_content</key>
<dict>
<key>begin</key>
<string>"</string>
<key>end</key>
<string>"</string>
<key>patterns</key>
<array>
<dict>
<key>include</key>
<string>#qq_double_string_content</string>
</dict>
</array>
</dict>
<key>qq_paren_string_content</key>
<dict>
<key>begin</key>
<string>\(</string>
<key>end</key>
<string>\)</string>
<key>patterns</key>
<array>
<dict>
<key>include</key>
<string>#qq_paren_string_content</string>
</dict>
</array>
</dict>
<key>qq_single_string_content</key>
<dict>
<key>begin</key>
<string>'</string>
<key>end</key>
<string>'</string>
<key>patterns</key>
<array>
<dict>
<key>include</key>
<string>#qq_single_string_content</string>
</dict>
</array>
</dict>
<key>qq_slash_string_content</key>
<dict>
<key>begin</key>
<string>\\/</string>
<key>end</key>
<string>\\/</string>
<key>patterns</key>
<array>
<dict>
<key>include</key>
<string>#qq_slash_string_content</string>
</dict>
</array>
</dict>
</dict>
<key>scopeName</key>
<string>source.perl.6</string>
<key>uuid</key>
<string>E685440C-0E20-4424-9693-864D5240A269</string>
</dict>
</plist>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,319 @@
{
"fileTypes": [
"p6",
"pl6",
"pm6",
"nqp"
],
"firstLineMatch": "(?x)\n# Hashbang\n^\\#!.*(?:\\s|\\/|(?<=!)\\b)(?:perl6|nqp)(?:$|\\s)\n|\n# Perl 6 pragma\n\\buse\\s+v6\\b\n|\n# Modeline\n(?i:\n # Emacs\n -\\*-(?:(?:(?!mode\\s*:)[^:;]+:[^:;]+;)*\\s*mode\\s*:)?\\s*\n\tperl6\n \\s*(?:;[^:;]+:[^:;]+?)*;?\\s*-\\*-\n |\n # Vim\n (?:(?:\\s|^)vi(?:m[<=>]?\\d+|m)?|(?!^)\\sex)(?=:(?=\\s*set?\\s[^\\n:]+:)|:(?!\\s*set?\\s))(?:(?:\\s|\\s*:\\s*)\\w*(?:\\s*=(?:[^\\n\\\\\\s]|\\\\.)*)?)*[\\s:](?:filetype|ft|syntax)\\s*=\n\tperl6\n (?=\\s|:|$)\n)",
"keyEquivalent": "^~P",
"name": "Perl 6",
"patterns": [
{
"begin": "^=begin",
"end": "^=end",
"name": "comment.block.perl"
},
{
"begin": "(^[ \\t]+)?(?=#)",
"beginCaptures": {
"1": {
"name": "punctuation.whitespace.comment.leading.perl"
}
},
"end": "(?!\\G)",
"patterns": [
{
"begin": "#",
"beginCaptures": {
"0": {
"name": "punctuation.definition.comment.perl"
}
},
"end": "\\n",
"name": "comment.line.number-sign.perl"
}
]
},
{
"captures": {
"1": {
"name": "storage.type.class.perl.6"
},
"3": {
"name": "entity.name.type.class.perl.6"
}
},
"match": "(class|enum|grammar|knowhow|module|package|role|slang|subset)(\\s+)(((?:::|')?(?:([a-zA-Z_\\x{C0}-\\x{FF}\\$])([a-zA-Z0-9_\\x{C0}-\\x{FF}\\\\$]|[\\-'][a-zA-Z0-9_\\x{C0}-\\x{FF}\\$])*))+)",
"name": "meta.class.perl.6"
},
{
"begin": "(?<=\\s)'",
"beginCaptures": {
"0": {
"name": "punctuation.definition.string.begin.perl"
}
},
"end": "'",
"endCaptures": {
"0": {
"name": "punctuation.definition.string.end.perl"
}
},
"name": "string.quoted.single.perl",
"patterns": [
{
"match": "\\\\['\\\\]",
"name": "constant.character.escape.perl"
}
]
},
{
"begin": "\"",
"beginCaptures": {
"0": {
"name": "punctuation.definition.string.begin.perl"
}
},
"end": "\"",
"endCaptures": {
"0": {
"name": "punctuation.definition.string.end.perl"
}
},
"name": "string.quoted.double.perl",
"patterns": [
{
"match": "\\\\[abtnfre\"\\\\]",
"name": "constant.character.escape.perl"
}
]
},
{
"begin": "q(q|to|heredoc)*\\s*:?(q|to|heredoc)*\\s*/(.+)/",
"end": "\\3",
"name": "string.quoted.single.heredoc.perl"
},
{
"begin": "(q|Q)(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\\s*:?(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\\s*{{",
"end": "}}",
"name": "string.quoted.double.heredoc.brace.perl",
"patterns": [
{
"include": "#qq_brace_string_content"
}
]
},
{
"begin": "(q|Q)(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\\s*:?(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\\s*\\(\\(",
"end": "\\)\\)",
"name": "string.quoted.double.heredoc.paren.perl",
"patterns": [
{
"include": "#qq_paren_string_content"
}
]
},
{
"begin": "(q|Q)(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\\s*:?(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\\s*\\[\\[",
"end": "\\]\\]",
"name": "string.quoted.double.heredoc.bracket.perl",
"patterns": [
{
"include": "#qq_bracket_string_content"
}
]
},
{
"begin": "(q|Q)(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\\s*:?(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\\s*{",
"end": "}",
"name": "string.quoted.single.heredoc.brace.perl",
"patterns": [
{
"include": "#qq_brace_string_content"
}
]
},
{
"begin": "(q|Q)(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\\s*:?(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\\s*/",
"end": "/",
"name": "string.quoted.single.heredoc.slash.perl",
"patterns": [
{
"include": "#qq_slash_string_content"
}
]
},
{
"begin": "(q|Q)(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\\s*:?(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\\s*\\(",
"end": "\\)",
"name": "string.quoted.single.heredoc.paren.perl",
"patterns": [
{
"include": "#qq_paren_string_content"
}
]
},
{
"begin": "(q|Q)(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\\s*:?(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\\s*\\[",
"end": "\\]",
"name": "string.quoted.single.heredoc.bracket.perl",
"patterns": [
{
"include": "#qq_bracket_string_content"
}
]
},
{
"begin": "(q|Q)(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\\s*:?(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\\s*'",
"end": "'",
"name": "string.quoted.single.heredoc.single.perl",
"patterns": [
{
"include": "#qq_single_string_content"
}
]
},
{
"begin": "(q|Q)(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\\s*:?(x|exec|w|words|ww|quotewords|v|val|q|single|qq|double|s|scalar|a|array|h|hash|f|function|c|closure|b|blackslash|regexp|substr|trans|codes|p|path)*\\s*\"",
"end": "\"",
"name": "string.quoted.single.heredoc.double.perl",
"patterns": [
{
"include": "#qq_double_string_content"
}
]
},
{
"match": "\\b\\$\\w+\\b",
"name": "variable.other.perl"
},
{
"match": "\\b(macro|sub|submethod|method|multi|proto|only|rule|token|regex|category)\\b",
"name": "storage.type.declare.routine.perl"
},
{
"match": "\\b(self)\\b",
"name": "variable.language.perl"
},
{
"match": "\\b(use|require)\\b",
"name": "keyword.other.include.perl"
},
{
"match": "\\b(if|else|elsif|unless)\\b",
"name": "keyword.control.conditional.perl"
},
{
"match": "\\b(let|my|our|state|temp|has|constant)\\b",
"name": "storage.type.variable.perl"
},
{
"match": "\\b(for|loop|repeat|while|until|gather|given)\\b",
"name": "keyword.control.repeat.perl"
},
{
"match": "\\b(take|do|when|next|last|redo|return|contend|maybe|defer|default|exit|make|continue|break|goto|leave|async|lift)\\b",
"name": "keyword.control.flowcontrol.perl"
},
{
"match": "\\b(is|as|but|trusts|of|returns|handles|where|augment|supersede)\\b",
"name": "storage.modifier.type.constraints.perl"
},
{
"match": "\\b(BEGIN|CHECK|INIT|START|FIRST|ENTER|LEAVE|KEEP|UNDO|NEXT|LAST|PRE|POST|END|CATCH|CONTROL|TEMP)\\b",
"name": "meta.function.perl"
},
{
"match": "\\b(die|fail|try|warn)\\b",
"name": "keyword.control.control-handlers.perl"
},
{
"match": "\\b(prec|irs|ofs|ors|export|deep|binary|unary|reparsed|rw|parsed|cached|readonly|defequiv|will|ref|copy|inline|tighter|looser|equiv|assoc|required)\\b",
"name": "storage.modifier.perl"
},
{
"match": "\\b(NaN|Inf)\\b",
"name": "constant.numeric.perl"
},
{
"match": "\\b(oo|fatal)\\b",
"name": "keyword.other.pragma.perl"
},
{
"match": "\\b(Object|Any|Junction|Whatever|Capture|MatchSignature|Proxy|Matcher|Package|Module|ClassGrammar|Scalar|Array|Hash|KeyHash|KeySet|KeyBagPair|List|Seq|Range|Set|Bag|Mapping|Void|UndefFailure|Exception|Code|Block|Routine|Sub|MacroMethod|Submethod|Regex|Str|str|Blob|Char|ByteCodepoint|Grapheme|StrPos|StrLen|Version|NumComplex|num|complex|Bit|bit|bool|True|FalseIncreasing|Decreasing|Ordered|Callable|AnyCharPositional|Associative|Ordering|KeyExtractorComparator|OrderingPair|IO|KitchenSink|RoleInt|int|int1|int2|int4|int8|int16|int32|int64Rat|rat|rat1|rat2|rat4|rat8|rat16|rat32|rat64Buf|buf|buf1|buf2|buf4|buf8|buf16|buf32|buf64UInt|uint|uint1|uint2|uint4|uint8|uint16|uint32uint64|Abstraction|utf8|utf16|utf32)\\b",
"name": "support.type.perl6"
},
{
"match": "\\b(div|xx|x|mod|also|leg|cmp|before|after|eq|ne|le|lt|not|gt|ge|eqv|ff|fff|and|andthen|or|xor|orelse|extra|lcm|gcd)\\b",
"name": "keyword.operator.perl"
},
{
"match": "(\\$|@|%|&)(\\*|:|!|\\^|~|=|\\?|(<(?=.+>)))?([a-zA-Z_\\x{C0}-\\x{FF}\\$])([a-zA-Z0-9_\\x{C0}-\\x{FF}\\$]|[\\-'][a-zA-Z0-9_\\x{C0}-\\x{FF}\\$])*",
"name": "variable.other.identifier.perl.6"
},
{
"match": "\\b(eager|hyper|substr|index|rindex|grep|map|sort|join|lines|hints|chmod|split|reduce|min|max|reverse|truncate|zip|cat|roundrobin|classify|first|sum|keys|values|pairs|defined|delete|exists|elems|end|kv|any|all|one|wrap|shape|key|value|name|pop|push|shift|splice|unshift|floor|ceiling|abs|exp|log|log10|rand|sign|sqrt|sin|cos|tan|round|strand|roots|cis|unpolar|polar|atan2|pick|chop|p5chop|chomp|p5chomp|lc|lcfirst|uc|ucfirst|capitalize|normalize|pack|unpack|quotemeta|comb|samecase|sameaccent|chars|nfd|nfc|nfkd|nfkc|printf|sprintf|caller|evalfile|run|runinstead|nothing|want|bless|chr|ord|gmtime|time|eof|localtime|gethost|getpw|chroot|getlogin|getpeername|kill|fork|wait|perl|graphs|codes|bytes|clone|print|open|read|write|readline|say|seek|close|opendir|readdir|slurp|spurt|shell|run|pos|fmt|vec|link|unlink|symlink|uniq|pair|asin|atan|sec|cosec|cotan|asec|acosec|acotan|sinh|cosh|tanh|asinh|done|acos|acosh|atanh|sech|cosech|cotanh|sech|acosech|acotanh|asech|ok|nok|plan_ok|dies_ok|lives_ok|skip|todo|pass|flunk|force_todo|use_ok|isa_ok|diag|is_deeply|isnt|like|skip_rest|unlike|cmp_ok|eval_dies_ok|nok_error|eval_lives_ok|approx|is_approx|throws_ok|version_lt|plan|EVAL|succ|pred|times|nonce|once|signature|new|connect|operator|undef|undefine|sleep|from|to|infix|postfix|prefix|circumfix|postcircumfix|minmax|lazy|count|unwrap|getc|pi|e|context|void|quasi|body|each|contains|rewinddir|subst|can|isa|flush|arity|assuming|rewind|callwith|callsame|nextwith|nextsame|attr|eval_elsewhere|none|srand|trim|trim_start|trim_end|lastcall|WHAT|WHERE|HOW|WHICH|VAR|WHO|WHENCE|ACCEPTS|REJECTS|not|true|iterator|by|re|im|invert|flip|gist|flat|tree|is-prime|throws_like|trans)\\b",
"name": "support.function.perl"
}
],
"repository": {
"qq_brace_string_content": {
"begin": "{",
"end": "}",
"patterns": [
{
"include": "#qq_brace_string_content"
}
]
},
"qq_bracket_string_content": {
"begin": "\\[",
"end": "\\]",
"patterns": [
{
"include": "#qq_bracket_string_content"
}
]
},
"qq_double_string_content": {
"begin": "\"",
"end": "\"",
"patterns": [
{
"include": "#qq_double_string_content"
}
]
},
"qq_paren_string_content": {
"begin": "\\(",
"end": "\\)",
"patterns": [
{
"include": "#qq_paren_string_content"
}
]
},
"qq_single_string_content": {
"begin": "'",
"end": "'",
"patterns": [
{
"include": "#qq_single_string_content"
}
]
},
"qq_slash_string_content": {
"begin": "\\\\/",
"end": "\\\\/",
"patterns": [
{
"include": "#qq_slash_string_content"
}
]
}
},
"scopeName": "source.perl.6",
"uuid": "E685440C-0E20-4424-9693-864D5240A269",
"version": "https://github.com/textmate/perl.tmbundle/commit/d9841a0878239fa43f88c640f8d458590f97e8f5"
}
+5
View File
@@ -9,26 +9,31 @@
"contributes": {
"themes": [
{
"id": "Default Dark+",
"label": "Dark+ (default dark)",
"uiTheme": "vs-dark",
"path": "./themes/dark_plus.json"
},
{
"id": "Default Light+",
"label": "Light+ (default light)",
"uiTheme": "vs",
"path": "./themes/light_plus.json"
},
{
"id": "Visual Studio Dark",
"label": "Dark (Visual Studio)",
"uiTheme": "vs-dark",
"path": "./themes/dark_vs.json"
},
{
"id": "Visual Studio Light",
"label": "Light (Visual Studio)",
"uiTheme": "vs",
"path": "./themes/light_vs.json"
},
{
"id": "Default High Contrast",
"label": "High Contrast",
"uiTheme": "hc-black",
"path": "./themes/hc_black.json"
+3 -3
View File
@@ -13,9 +13,9 @@
"resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz"
},
"typescript": {
"version": "typescript@2.2.1-insiders.20170209",
"from": "typescript@typescript@2.2.1-insiders.20170209",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.2.1-insiders.20170209.tgz"
"version": "typescript@2.2.1-insiders.20170216",
"from": "typescript@typescript@2.2.1-insiders.20170216",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.2.1-insiders.20170216.tgz"
},
"vscode-extension-telemetry": {
"version": "0.0.5",
+36 -6
View File
@@ -14,7 +14,7 @@
"semver": "4.3.6",
"vscode-extension-telemetry": "^0.0.5",
"vscode-nls": "^2.0.1",
"typescript": "typescript@2.2.1-insiders.20170209"
"typescript": "typescript@2.2.1-insiders.20170216"
},
"devDependencies": {
"@types/node": "^7.0.4",
@@ -33,7 +33,12 @@
"onLanguage:jsx-tags",
"onCommand:typescript.reloadProjects",
"onCommand:javascript.reloadProjects",
"onCommand:typescript.selectTypeScriptVersion"
"onCommand:typescript.selectTypeScriptVersion",
"onCommand:javascript.goToProjectConfig",
"onCommand:typescript.goToProjectConfig",
"onCommand:_typescript.tryCompleteJsDoc",
"workspaceContains:jsconfig.json",
"workspaceContains:tsconfig.json"
],
"main": "./out/typescriptMain",
"enableProposedApi": true,
@@ -79,6 +84,18 @@
}
}
],
"keybindings": [
{
"key": "enter",
"command": "_typescript.tryCompleteJsDoc",
"when": "editorTextFocus && !suggestWidgetVisible && editorLangId == 'typescript'"
},
{
"key": "enter",
"command": "_typescript.tryCompleteJsDoc",
"when": "editorTextFocus && !suggestWidgetVisible && editorLangId == 'javascript'"
}
],
"configuration": {
"type": "object",
"title": "%configuration.typescript%",
@@ -257,15 +274,28 @@
"commands": [
{
"command": "typescript.reloadProjects",
"title": "%typescript.reloadProjects.title%"
"title": "%typescript.reloadProjects.title%",
"category": "TypeScript"
},
{
"command": "javascript.reloadProjects",
"title": "%javascript.reloadProjects.title%"
"title": "%javascript.reloadProjects.title%",
"category": "JavaScript"
},
{
"command": "typescript.selectTypeScriptVersion",
"title": "%typescript.selectTypeScriptVersion.title%"
"title": "%typescript.selectTypeScriptVersion.title%",
"category": "TypeScript"
},
{
"command": "typescript.goToProjectConfig",
"title": "%typescript.goToProjectConfig.title%",
"category": "TypeScript"
},
{
"command": "javascript.goToProjectConfig",
"title": "%javascript.goToProjectConfig.title%",
"category": "JavaScript"
}
],
"breakpoints": [
@@ -301,4 +331,4 @@
}
]
}
}
}
+6 -4
View File
@@ -1,6 +1,6 @@
{
"typescript.reloadProjects.title": "Reload TypeScript Project",
"javascript.reloadProjects.title": "Reload JavaScript Project",
"typescript.reloadProjects.title": "Reload Project",
"javascript.reloadProjects.title": "Reload Project",
"configuration.typescript": "TypeScript",
"typescript.useCodeSnippetsOnMethodSuggest.dec": "Complete functions with their parameter signature.",
"typescript.tsdk.desc": "Specifies the folder path containing the tsserver and lib*.d.ts files to use.",
@@ -24,6 +24,8 @@
"format.placeOpenBraceOnNewLineForFunctions": "Defines whether an open brace is put onto a new line for functions or not.",
"format.placeOpenBraceOnNewLineForControlBlocks": "Defines whether an open brace is put onto a new line for control blocks or not.",
"javascript.validate.enable": "Enable/disable JavaScript validation.",
"typescript.referencesCodeLens.enabled": "Enable/disable the references code lens.",
"typescript.selectTypeScriptVersion.title": "Select TypeScript version."
"typescript.goToProjectConfig.title": "Go to Project Configuration",
"javascript.goToProjectConfig.title": "Go to Project Configuration",
"typescript.referencesCodeLens.enabled": "Enable/disable references CodeLens.",
"typescript.selectTypeScriptVersion.title": "Select TypeScript Version"
}
@@ -153,8 +153,12 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
public provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken): Promise<CompletionItem[]> {
if (this.typingsStatus.isAcquiringTypings) {
return Promise.reject<CompletionItem[]>({
label: localize('acquiringTypingsLabel', 'Acquiring typings...'),
detail: localize('acquiringTypingsDetail', 'Acquiring typings definitions for IntelliSense.')
label: localize(
{ key: 'acquiringTypingsLabel', comment: ['Typings refers to the *.d.ts typings files that power our IntelliSense. It should not be localized'] },
'Acquiring typings...'),
detail: localize(
{ key: 'acquiringTypingsDetail', comment: ['Typings refers to the *.d.ts typings files that power our IntelliSense. It should not be localized'] },
'Acquiring typings definitions for IntelliSense.')
});
}
+106 -1
View File
@@ -9,12 +9,13 @@
* ------------------------------------------------------------------------------------------ */
'use strict';
import { env, languages, commands, workspace, window, ExtensionContext, Memento, IndentAction, Diagnostic, DiagnosticCollection, Range, DocumentFilter, Disposable, Uri } from 'vscode';
import { env, languages, commands, workspace, window, ExtensionContext, Memento, IndentAction, Diagnostic, DiagnosticCollection, Range, DocumentFilter, Disposable, Uri, MessageItem, TextEditor } from 'vscode';
// This must be the first statement otherwise modules might got loaded with
// the wrong locale.
import * as nls from 'vscode-nls';
nls.config({ locale: env.language });
const localize = nls.loadMessageBundle();
import * as path from 'path';
@@ -39,6 +40,7 @@ import WorkspaceSymbolProvider from './features/workspaceSymbolProvider';
import CodeActionProvider from './features/codeActionProvider';
import ReferenceCodeLensProvider from './features/referencesCodeLensProvider';
import JsDocCompletionHelper from './utils/JsDocCompletionHelper';
import * as BuildStatus from './utils/buildStatus';
import * as ProjectStatus from './utils/projectStatus';
import TypingsStatus, { AtaProgressReporter } from './utils/typingsStatus';
@@ -52,6 +54,16 @@ interface LanguageDescription {
configFile: string;
}
enum ProjectConfigAction {
None,
CreateConfig,
LearnMore
}
interface ProjectConfigMessageItem extends MessageItem {
id: ProjectConfigAction;
}
export function activate(context: ExtensionContext): void {
const MODE_ID_TS = 'typescript';
const MODE_ID_TSX = 'typescriptreact';
@@ -89,6 +101,29 @@ export function activate(context: ExtensionContext): void {
client.onVersionStatusClicked();
}));
const jsDocCompletionHelper = new JsDocCompletionHelper(client);
context.subscriptions.push(commands.registerCommand('_typescript.tryCompleteJsDoc', () => {
const editor = window.activeTextEditor;
if (!editor || !editor.selection.isEmpty) {
return commands.executeCommand('type', { text: '\n' });
}
return jsDocCompletionHelper.tryCompleteJsDoc(editor, editor.selection.active).then(didCompleteComment => {
if (didCompleteComment) {
return;
}
return commands.executeCommand('type', { text: '\n' });
});
}));
const goToProjectConfig = (isTypeScript: boolean) => {
const editor = window.activeTextEditor;
if (editor) {
clientHost.goToProjectConfig(isTypeScript, editor.document.uri, editor.document.languageId);
}
};
context.subscriptions.push(commands.registerCommand('typescript.goToProjectConfig', goToProjectConfig.bind(null, true)));
context.subscriptions.push(commands.registerCommand('javascript.goToProjectConfig', goToProjectConfig.bind(null, false)));
window.onDidChangeActiveTextEditor(VersionStatus.showHideStatus, null, context.subscriptions);
client.onReady().then(() => {
context.subscriptions.push(ProjectStatus.create(client,
@@ -378,6 +413,76 @@ class TypeScriptServiceClientHost implements ITypescriptServiceClientHost {
return !!this.findLanguage(file);
}
public goToProjectConfig(
isTypeScriptProject: boolean,
resource: Uri,
languageId: string
): Thenable<TextEditor> | undefined {
const rootPath = workspace.rootPath;
if (!rootPath) {
window.showInformationMessage(
localize(
'typescript.projectConfigNoWorkspace',
'Please open a folder in VS Code to use a TypeScript or JavaScript project'));
return;
}
const file = this.client.normalizePath(resource);
// TODO: TSServer errors when 'projectInfo' is invoked on a non js/ts file
if (!file || !this.languagePerId[languageId]) {
window.showWarningMessage(
localize(
'typescript.projectConfigUnsupportedFile',
'Could not determine TypeScript or JavaScript project. Unsupported file type'));
return;
}
return this.client.execute('projectInfo', { file, needFileNameList: false }).then(res => {
if (!res || !res.body) {
return window.showWarningMessage(localize('typescript.projectConfigCouldNotGetInfo', 'Could not determine TypeScript or JavaScript project'));
}
const { configFileName } = res.body;
if (configFileName && configFileName.indexOf('/dev/null/') !== 0) {
return workspace.openTextDocument(configFileName)
.then(window.showTextDocument);
}
return window.showInformationMessage<ProjectConfigMessageItem>(
(isTypeScriptProject
? localize('typescript.noTypeScriptProjectConfig', 'File is not part of a TypeScript project')
: localize('typescript.noJavaScriptProjectConfig', 'File is not part of a JavaScript project')
), {
title: isTypeScriptProject
? localize('typescript.configureTsconfigQuickPick', 'Configure tsconfig.json')
: localize('typescript.configureJsconfigQuickPick', 'Configure jsconfig.json'),
id: ProjectConfigAction.CreateConfig
}, {
title: localize('typescript.projectConfigLearnMore', 'Learn More'),
id: ProjectConfigAction.LearnMore
}).then(selected => {
switch (selected && selected.id) {
case ProjectConfigAction.CreateConfig:
const configFile = Uri.file(path.join(rootPath, isTypeScriptProject ? 'tsconfig.json' : 'jsconfig.json'));
return workspace.openTextDocument(configFile)
.then(undefined, _ => workspace.openTextDocument(configFile.with({ scheme: 'untitled' })))
.then(window.showTextDocument);
case ProjectConfigAction.LearnMore:
if (isTypeScriptProject) {
commands.executeCommand('vscode.open', Uri.parse('https://go.microsoft.com/fwlink/?linkid=841896'));
} else {
commands.executeCommand('vscode.open', Uri.parse('https://go.microsoft.com/fwlink/?linkid=759670'));
}
return;
default:
return Promise.resolve(undefined);
}
});
});
}
private findLanguage(file: string): LanguageProvider | null {
for (let i = 0; i < this.languages.length; i++) {
let language = this.languages[i];
@@ -103,6 +103,7 @@ export interface ITypescriptServiceClient {
execute(command: 'navtree', args: Proto.FileRequestArgs, token?: CancellationToken): Promise<Proto.NavTreeResponse>;
execute(command: 'getCodeFixes', args: Proto.CodeFixRequestArgs, token?: CancellationToken): Promise<Proto.GetCodeFixesResponse>;
execute(command: 'getSupportedCodeFixes', args: null, token?: CancellationToken): Promise<Proto.GetSupportedCodeFixesResponse>;
execute(command: 'docCommentTemplate', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise<Proto.DocCommandTemplateResponse>;
// execute(command: 'compileOnSaveAffectedFileList', args: Proto.CompileOnSaveEmitFileRequestArgs, token?: CancellationToken): Promise<Proto.CompileOnSaveAffectedFileListResponse>;
// execute(command: 'compileOnSaveEmitFile', args: Proto.CompileOnSaveEmitFileRequestArgs, token?: CancellationToken): Promise<any>;
execute(command: string, args: any, expectedResult: boolean | CancellationToken, token?: CancellationToken): Promise<any>;
@@ -0,0 +1,97 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TextEditor, Position, Range, Selection } from 'vscode';
import { ITypescriptServiceClient } from '../typescriptService';
import { FileLocationRequestArgs, DocCommandTemplateResponse } from '../protocol';
export default class JsDocCompletionHelper {
constructor(
private client: ITypescriptServiceClient,
) { }
public tryCompleteJsDoc(editor: TextEditor, position: Position): Thenable<boolean> {
const file = this.client.normalizePath(editor.document.uri);
if (!file) {
return Promise.resolve(false);
}
const line = editor.document.lineAt(position.line).text;
// Ensure line starts with '/**' then cursor
const prefix = line.slice(0, position.character).match(/^\s*(\/\*\*+)$/);
if (!prefix) {
return Promise.resolve(false);
}
// Ensure there is no content after the cursor besides possibly the end of the comment
const suffix = line.slice(position.character).match(/^\s*\**\/?$/);
if (!suffix) {
return Promise.resolve(false);
}
const start = position.translate(0, -prefix[1].length);
return editor.edit(
edits => {
edits.delete(new Range(start, new Position(start.line, line.length)));
}, {
undoStopBefore: true,
undoStopAfter: false
}
).then(removedComment => {
if (!removedComment) {
// Edit failed, nothing to revert.
return false;
}
const args: FileLocationRequestArgs = {
file: file,
line: start.line + 1,
offset: start.character + 1
};
return Promise.race([
this.client.execute('docCommentTemplate', args),
new Promise((_, reject) => {
setTimeout(reject, 250);
})
]).then((res: DocCommandTemplateResponse) => {
if (!res || !res.body) {
return false;
}
const commentText = res.body.newText;
return editor.edit(
edits => edits.insert(start, commentText),
{ undoStopBefore: false, undoStopAfter: true });
}, () => {
return false;
}).then(didInsertComment => {
if (didInsertComment) {
const newCursorPosition = new Position(start.line + 1, editor.document.lineAt(start.line + 1).text.length);
editor.selection = new Selection(newCursorPosition, newCursorPosition);
return true;
}
// Revert to the original line content and restore position
return editor.edit(
edits => {
edits.insert(start, prefix[1] + suffix[0]);
}, {
undoStopBefore: false,
undoStopAfter: true
}
).then(() => {
editor.selection = new Selection(position, position);
return false;
});
});
});
}
}
+8 -13
View File
@@ -333,19 +333,14 @@
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz"
},
"node-pty": {
"version": "0.6.1",
"from": "node-pty@0.6.1",
"resolved": "https://registry.npmjs.org/node-pty/-/node-pty-0.6.1.tgz",
"version": "0.6.2",
"from": "node-pty@0.6.2",
"resolved": "https://registry.npmjs.org/node-pty/-/node-pty-0.6.2.tgz",
"dependencies": {
"extend": {
"version": "1.2.1",
"from": "extend@>=1.2.1 <1.3.0",
"resolved": "https://registry.npmjs.org/extend/-/extend-1.2.1.tgz"
},
"nan": {
"version": "2.2.1",
"from": "nan@2.2.1",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.2.1.tgz"
"version": "2.5.0",
"from": "nan@2.5.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.5.0.tgz"
}
}
},
@@ -430,9 +425,9 @@
"resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.0.tgz"
},
"xterm": {
"version": "2.2.3",
"version": "2.3.0",
"from": "Tyriar/xterm.js#vscode-release/1.10",
"resolved": "git+https://github.com/Tyriar/xterm.js.git#90cd66bf353b86ad52d7b650760d8d879dd1c7b8"
"resolved": "git+https://github.com/Tyriar/xterm.js.git#5513303451202b0135601a2f026602ed391b3906"
},
"yauzl": {
"version": "2.3.1",
+2 -2
View File
@@ -2,7 +2,7 @@
"name": "code-oss-dev",
"version": "1.10.0",
"electronVersion": "1.4.6",
"distro": "0edfcc54d5e8654b41e73aacb85adeda2461dcfe",
"distro": "fdc80c6b4d95e5f961bd3f85b927693915550942",
"author": {
"name": "Microsoft Corporation"
},
@@ -31,7 +31,7 @@
"iconv-lite": "0.4.15",
"minimist": "1.2.0",
"native-keymap": "0.3.0",
"node-pty": "0.6.1",
"node-pty": "0.6.2",
"semver": "4.3.6",
"vscode-debugprotocol": "1.16.0",
"vscode-textmate": "3.1.0",
+1
View File
@@ -11,6 +11,7 @@
"win32RegValueName": "CodeOSS",
"win32AppId": "{{E34003BB-9E10-4501-8C11-BE3FAA83F23F}",
"win32AppUserModelId": "Microsoft.CodeOSS",
"win32ShellNameShort": "C&ode - OSS",
"darwinBundleIdentifier": "com.visualstudio.code.oss",
"reportIssueUrl": "https://github.com/Microsoft/vscode/issues/new",
"urlProtocol": "code-oss"
+3 -4
View File
@@ -30,10 +30,9 @@ if [ "@@NAME@@" != "code-oss" ]; then
APT_DIR=$(get_apt_config_value Dir)
APT_ETC=$APT_DIR$(get_apt_config_value Dir::Etc)
APT_SOURCE_PARTS=$APT_ETC$(get_apt_config_value Dir::Etc::sourceparts)
CODE_SOURCE_LIST=$APT_SOURCE_PARTS/visual-studio-@@NAME@@.list
APT_SOURCE_PARTS=$APT_ETC/$(get_apt_config_value Dir::Etc::sourceparts)
CODE_SOURCE_LIST=$APT_SOURCE_PARTS/vscode.list
rm -f $CODE_SOURCE_LIST
# TODO: #2973: Enable once the apt repository is signed
#echo "deb @@UPDATEURL@@/api/deb/@@QUALITY@@/@@ARCHITECTURE@@/ /" > $CODE_SOURCE_LIST
# echo "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main" > $CODE_SOURCE_LIST
fi
+1 -2
View File
@@ -1,5 +1,4 @@
#!/bin/bash
set -e
if [[ "$OSTYPE" == "darwin"* ]]; then
realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; }
@@ -19,7 +18,7 @@ else
fi
INTENDED_VERSION="v`node -p "require('./package.json').electronVersion"`"
INSTALLED_VERSION=`cat .build/electron/version 2> /dev/null`
INSTALLED_VERSION=$(cat .build/electron/version 2> /dev/null)
# Node modules
test -d node_modules || ./scripts/npm.sh install
+5 -10
View File
@@ -21,17 +21,12 @@ export interface IListElementEvent<T, E> {
event: E;
}
export interface IListEvent<T> {
elements: T[];
indexes: number[];
}
export interface IListMouseEvent<T> extends MouseEvent {
element: T;
index: number;
}
export interface IFocusChangeEvent<T> {
elements: T[];
indexes: number[];
}
export interface ISelectionChangeEvent<T> {
elements: T[];
indexes: number[];
}
+4 -4
View File
@@ -6,7 +6,7 @@
import 'vs/css!./list';
import { IDisposable } from 'vs/base/common/lifecycle';
import { range } from 'vs/base/common/arrays';
import { IDelegate, IRenderer, IFocusChangeEvent, ISelectionChangeEvent } from './list';
import { IDelegate, IRenderer, IListEvent } from './list';
import { List, IListOptions } from './listWidget';
import { IPagedModel } from 'vs/base/common/paging';
import Event, { mapEvent } from 'vs/base/common/event';
@@ -67,7 +67,7 @@ export class PagedList<T> {
container: HTMLElement,
delegate: IDelegate<number>,
renderers: IPagedRenderer<T, any>[],
options: IListOptions = {}
options: IListOptions<any> = {} // TODO@Joao: should be IListOptions<T>
) {
const pagedRenderers = renderers.map(r => new PagedRenderer<T, ITemplateData<T>>(r, () => this.model));
this.list = new List(container, delegate, pagedRenderers, options);
@@ -77,11 +77,11 @@ export class PagedList<T> {
return this.list;
}
get onFocusChange(): Event<IFocusChangeEvent<T>> {
get onFocusChange(): Event<IListEvent<T>> {
return mapEvent(this.list.onFocusChange, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes }));
}
get onSelectionChange(): Event<ISelectionChangeEvent<T>> {
get onSelectionChange(): Event<IListEvent<T>> {
return mapEvent(this.list.onSelectionChange, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes }));
}
+92 -45
View File
@@ -13,9 +13,26 @@ import { KeyCode } from 'vs/base/common/keyCodes';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import Event, { Emitter, EventBufferer, chain, mapEvent, fromCallback } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
import { IDelegate, IRenderer, IListMouseEvent, IFocusChangeEvent, ISelectionChangeEvent } from './list';
import { IDelegate, IRenderer, IListEvent, IListMouseEvent } from './list';
import { ListView, IListViewOptions } from './listView';
export interface IIdentityProvider<T> {
(element: T): string;
}
export interface ISpliceable<T> {
splice(start: number, deleteCount: number, elements: T[]): void;
}
class CombinedSpliceable<T> implements ISpliceable<T> {
constructor(private spliceables: ISpliceable<T>[]) { }
splice(start: number, deleteCount: number, elements: T[]): void {
this.spliceables.forEach(s => s.splice(start, deleteCount, elements));
}
}
interface ITraitTemplateData<D> {
container: HTMLElement;
data: D;
@@ -52,7 +69,7 @@ class TraitRenderer<T, D> implements IRenderer<T, ITraitTemplateData<D>>
}
}
class Trait<T> implements IDisposable {
class Trait<T> implements ISpliceable<boolean>, IDisposable {
private indexes: number[];
@@ -63,21 +80,16 @@ class Trait<T> implements IDisposable {
this.indexes = [];
}
splice(start: number, deleteCount: number, insertCount: number): void {
const diff = insertCount - deleteCount;
splice(start: number, deleteCount: number, elements: boolean[]): void {
const diff = elements.length - deleteCount;
const end = start + deleteCount;
const indexes: number[] = [];
const indexes = [
...this.indexes.filter(i => i < start),
...elements.reduce((r, hasTrait, i) => hasTrait ? [...r, i + start] : r, []),
...this.indexes.filter(i => i >= end).map(i => i + diff)
];
for (let index of indexes) {
if (index >= start && index < end) {
continue;
}
indexes.push(index > start ? index + diff : index);
}
this.indexes = indexes;
this._onChange.fire({ indexes });
this.set(indexes);
}
renderElement(element: T, index: number, container: HTMLElement): void {
@@ -111,14 +123,41 @@ class Trait<T> implements IDisposable {
class FocusTrait<T> extends Trait<T> {
constructor(private getElementId: (number: number) => string) {
constructor(
private getDomId: IIdentityProvider<number>
) {
super('focused');
}
renderElement(element: T, index: number, container: HTMLElement): void {
super.renderElement(element, index, container);
container.setAttribute('role', 'treeitem');
container.setAttribute('id', this.getElementId(index));
container.setAttribute('id', this.getDomId(index));
}
}
/**
* The TraitSpliceable is used as a util class to be able
* to preserve traits across splice calls, given an identity
* provider.
*/
class TraitSpliceable<T> implements ISpliceable<T> {
constructor(
private trait: Trait<T>,
private view: ListView<T>,
private getId?: IIdentityProvider<T>
) { }
splice(start: number, deleteCount: number, elements: T[]): void {
if (!this.getId) {
return this.trait.splice(start, deleteCount, elements.map(e => false));
}
const pastElementsWithTrait = this.trait.get().map(i => this.getId(this.view.element(i)));
const elementsWithTrait = elements.map(e => pastElementsWithTrait.indexOf(this.getId(e)) > -1);
this.trait.splice(start, deleteCount, elementsWithTrait);
}
}
@@ -144,7 +183,7 @@ class KeyboardController<T> implements IDisposable {
private onEnter(e: StandardKeyboardEvent): void {
e.preventDefault();
e.stopPropagation();
this.list.setSelection(this.list.getFocus());
this.list.open(this.list.getFocus());
}
private onUpArrow(e: StandardKeyboardEvent): void {
@@ -185,6 +224,7 @@ class KeyboardController<T> implements IDisposable {
}
class MouseController<T> implements IDisposable {
private disposables: IDisposable[];
constructor(
@@ -207,7 +247,7 @@ class MouseController<T> implements IDisposable {
e.stopPropagation();
this.view.domNode.focus();
this.list.setFocus([e.index]);
this.list.setSelection([e.index]);
this.list.open([e.index]);
}
dispose() {
@@ -215,18 +255,19 @@ class MouseController<T> implements IDisposable {
}
}
export interface IListOptions extends IListViewOptions {
export interface IListOptions<T> extends IListViewOptions {
identityProvider?: IIdentityProvider<T>;
ariaLabel?: string;
mouseSupport?: boolean;
keyboardSupport?: boolean;
}
const DefaultOptions: IListOptions = {
const DefaultOptions: IListOptions<any> = {
keyboardSupport: true,
mouseSupport: true
};
export class List<T> implements IDisposable {
export class List<T> implements ISpliceable<T>, IDisposable {
private static InstanceCount = 0;
private idPrefix = `list_id_${++List.InstanceCount}`;
@@ -235,25 +276,26 @@ export class List<T> implements IDisposable {
private selection: Trait<T>;
private eventBufferer: EventBufferer;
private view: ListView<T>;
private mouseController: MouseController<T>;
private keyboardController: KeyboardController<T>;
private spliceable: ISpliceable<T>;
private disposables: IDisposable[];
@memoize
get onFocusChange(): Event<IFocusChangeEvent<T>> {
return this.eventBufferer.wrapEvent(mapEvent(this.focus.onChange, e => this.toListEvent(e)));
@memoize get onFocusChange(): Event<IListEvent<T>> {
return mapEvent(this.eventBufferer.wrapEvent(this.focus.onChange), e => this.toListEvent(e));
}
@memoize
get onSelectionChange(): Event<ISelectionChangeEvent<T>> {
return this.eventBufferer.wrapEvent(mapEvent(this.selection.onChange, e => this.toListEvent(e)));
@memoize get onSelectionChange(): Event<IListEvent<T>> {
return mapEvent(this.eventBufferer.wrapEvent(this.selection.onChange), e => this.toListEvent(e));
}
@memoize
get onContextMenu(): Event<IListMouseEvent<T>> {
@memoize get onContextMenu(): Event<IListMouseEvent<T>> {
return fromCallback(handler => this.view.addListener('contextmenu', handler));
}
private _onOpen = new Emitter<number[]>();
@memoize get onOpen(): Event<IListEvent<T>> {
return mapEvent(this._onOpen.event, indexes => this.toListEvent({ indexes }));
}
private _onDOMFocus = new Emitter<void>();
get onDOMFocus(): Event<void> { return this._onDOMFocus.event; }
@@ -267,9 +309,9 @@ export class List<T> implements IDisposable {
container: HTMLElement,
delegate: IDelegate<T>,
renderers: IRenderer<T, any>[],
options: IListOptions = DefaultOptions
options: IListOptions<T> = DefaultOptions
) {
this.focus = new FocusTrait(i => this.getElementId(i));
this.focus = new FocusTrait(i => this.getElementDomId(i));
this.selection = new Trait('selected');
this.eventBufferer = new EventBufferer();
@@ -283,6 +325,12 @@ export class List<T> implements IDisposable {
this.view.domNode.setAttribute('role', 'tree');
this.view.domNode.tabIndex = 0;
this.spliceable = new CombinedSpliceable([
new TraitSpliceable(this.focus, this.view, options.identityProvider),
new TraitSpliceable(this.selection, this.view, options.identityProvider),
this.view
]);
this.disposables = [this.focus, this.selection, this.view, this._onDispose];
const tracker = DOM.trackFocus(this.view.domNode);
@@ -290,13 +338,11 @@ export class List<T> implements IDisposable {
this.disposables.push(tracker.addBlurListener(() => this._onDOMBlur.fire()));
if (typeof options.keyboardSupport !== 'boolean' || options.keyboardSupport) {
this.keyboardController = new KeyboardController(this, this.view);
this.disposables.push(this.keyboardController);
this.disposables.push(new KeyboardController(this, this.view));
}
if (typeof options.mouseSupport !== 'boolean' || options.mouseSupport) {
this.mouseController = new MouseController(this, this.view);
this.disposables.push(this.mouseController);
this.disposables.push(new MouseController(this, this.view));
}
this.onFocusChange(this._onFocusChange, this, this.disposables);
@@ -307,11 +353,7 @@ export class List<T> implements IDisposable {
}
splice(start: number, deleteCount: number, elements: T[] = []): void {
this.eventBufferer.bufferEvents(() => {
this.focus.splice(start, deleteCount, elements.length);
this.selection.splice(start, deleteCount, elements.length);
this.view.splice(start, deleteCount, elements);
});
this.eventBufferer.bufferEvents(() => this.spliceable.splice(start, deleteCount, elements));
}
get length(): number {
@@ -461,7 +503,7 @@ export class List<T> implements IDisposable {
}
}
getElementId(index: number): string {
private getElementDomId(index: number): string {
return `${this.idPrefix}_${index}`;
}
@@ -473,6 +515,11 @@ export class List<T> implements IDisposable {
return this.view.domNode;
}
open(indexes: number[]): void {
this.setSelection(indexes);
this._onOpen.fire(indexes);
}
private toListEvent({ indexes }: ITraitChangeEvent) {
return { indexes, elements: indexes.map(i => this.view.element(i)) };
}
@@ -481,7 +528,7 @@ export class List<T> implements IDisposable {
const focus = this.focus.get();
if (focus.length > 0) {
this.view.domNode.setAttribute('aria-activedescendant', this.getElementId(focus[0]));
this.view.domNode.setAttribute('aria-activedescendant', this.getElementDomId(focus[0]));
} else {
this.view.domNode.removeAttribute('aria-activedescendant');
}
+2 -1
View File
@@ -9,6 +9,7 @@ import { IEventEmitter, EventEmitter } from 'vs/base/common/eventEmitter';
import { IDisposable } from 'vs/base/common/lifecycle';
import * as Events from 'vs/base/common/events';
import Event, { Emitter } from 'vs/base/common/event';
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
export interface IAction extends IDisposable {
id: string;
@@ -202,7 +203,7 @@ export class Action implements IAction {
this._order = value;
}
public run(event?: any): TPromise<any> {
public run(event?: any, data?: ITelemetryData): TPromise<any> {
if (this._actionCallback !== void 0) {
return this._actionCallback(event);
}
+12 -2
View File
@@ -99,6 +99,17 @@ function hsla2rgba(hsla: HSLA): RGBA {
return { r: Math.round(r * 255), g: Math.round(g * 255), b: Math.round(b * 255), a };
}
export function hexToCSS(hex: string) {
if (hex.length === 9) {
return toCSSColor(hex2rgba(hex));
}
return hex;
}
function toCSSColor(rgba: RGBA): string {
return `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${+rgba.a.toFixed(2)})`;
}
export class Color {
private rgba: RGBA;
@@ -194,8 +205,7 @@ export class Color {
public toString(): string {
if (!this.str) {
let p = this.rgba;
this.str = `rgba(${p.r}, ${p.g}, ${p.b}, ${+p.a.toFixed(2)})`;
this.str = toCSSColor(this.rgba);
}
return this.str;
}
+1 -1
View File
@@ -63,7 +63,7 @@ export function setProperty(text: string, path: JSONPath, value: any, formatting
}
} else {
if (value === void 0) { // delete
throw new Error(`Property ${lastSegment} does not exist.`);
return []; // property does not exist, nothing to do
}
let newProperty = `${JSON.stringify(lastSegment)}: ${JSON.stringify(value)}`;
let index = getInsertionIndex ? getInsertionIndex(parent.children.map(p => p.children[0].value)) : parent.children.length;
+14 -4
View File
@@ -21,6 +21,7 @@ import { getCommonHTTPHeaders } from 'vs/platform/environment/node/http';
import { IWindowSettings, MenuBarVisibility } from 'vs/platform/windows/common/windows';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
export interface IWindowState {
width?: number;
height?: number;
@@ -78,6 +79,7 @@ export interface IWindowConfiguration extends ParsedArgs {
zoomLevel?: number;
fullscreen?: boolean;
highContrast?: boolean;
baseTheme?: string;
accessibilitySupport?: boolean;
isInitialStartup?: boolean;
@@ -134,7 +136,7 @@ export interface IVSCodeWindow {
export class VSCodeWindow implements IVSCodeWindow {
public static colorThemeStorageKey = 'theme';
public static themeStorageKey = 'theme';
private static MIN_WIDTH = 200;
private static MIN_HEIGHT = 120;
@@ -177,9 +179,9 @@ export class VSCodeWindow implements IVSCodeWindow {
this.restoreWindowState(config.state);
// For VS theme we can show directly because background is white
const themeId = this.storageService.getItem<string>(VSCodeWindow.colorThemeStorageKey);
const usesLightTheme = /vs($| )/.test(themeId);
const usesHighContrastTheme = /hc-black($| )/.test(themeId) || (platform.isWindows && systemPreferences.isInvertedColorScheme());
const baseTheme = this.getBaseTheme();
const usesLightTheme = 'vs' === baseTheme;
const usesHighContrastTheme = 'hc-black' === baseTheme || (platform.isWindows && systemPreferences.isInvertedColorScheme());
// in case we are maximized or fullscreen, only show later after the call to maximize/fullscreen (see below)
const isFullscreenOrMaximized = (this.currentWindowMode === WindowMode.Maximized || this.currentWindowMode === WindowMode.Fullscreen);
@@ -505,6 +507,9 @@ export class VSCodeWindow implements IVSCodeWindow {
windowConfiguration.highContrast = platform.isWindows && systemPreferences.isInvertedColorScheme() && (!windowConfig || windowConfig.autoDetectHighContrast);
windowConfiguration.accessibilitySupport = app.isAccessibilitySupportEnabled();
// Theme
windowConfiguration.baseTheme = this.getBaseTheme();
// Perf Counters
windowConfiguration.perfStartTime = global.perfStartTime;
windowConfiguration.perfAppReady = global.perfAppReady;
@@ -524,6 +529,11 @@ export class VSCodeWindow implements IVSCodeWindow {
return url;
}
private getBaseTheme(): string {
const theme = this.storageService.getItem<string>(VSCodeWindow.themeStorageKey, 'vs-dark');
return theme.split(' ')[0];
}
public serializeWindowState(): IWindowState {
if (this.win.isFullScreen()) {
return {
+20 -53
View File
@@ -26,6 +26,7 @@ import { ILogService } from 'vs/code/electron-main/log';
import { getPathLabel } from 'vs/base/common/labels';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IWindowSettings } from 'vs/platform/windows/common/windows';
import { getLastActiveWindow, OpenContext as OriginalOpenContext, findBestWindowOrFolder } from 'vs/code/node/windowsUtils';
import CommonEvent, { Emitter } from 'vs/base/common/event';
import product from 'vs/platform/node/product';
@@ -34,26 +35,8 @@ enum WindowError {
CRASHED
}
export enum OpenContext {
// opening when running from the command line
CLI,
// macOS only: opening from the dock (also when opening files to a running instance from desktop)
DOCK,
// opening from the main application window
MENU,
// opening from a file or folder dialog
DIALOG,
// opening from the OS's UI
DESKTOP,
// opening through the API
API
}
export const OpenContext = OriginalOpenContext;
export type OpenContext = OriginalOpenContext;
export interface IOpenConfiguration {
context: OpenContext;
@@ -292,10 +275,9 @@ export class WindowsManager implements IWindowsMainService {
// Theme changes
if (event === 'vscode:changeColorTheme' && typeof payload === 'string') {
this.storageService.setItem(VSCodeWindow.colorThemeStorageKey, payload);
this.storageService.setItem(VSCodeWindow.themeStorageKey, payload);
}
}
public reload(win: VSCodeWindow, cli?: ParsedArgs): void {
// Only reload when the window has not vetoed this
@@ -409,20 +391,28 @@ export class WindowsManager implements IWindowsMainService {
}
// Open Files in last instance if any and flag tells us so
let bestWindow;
if (!openFilesInNewWindow && (bestWindow = this.findBestWindow(openConfig.context, [...filesToOpen, ...filesToCreate, ...filesToDiff]))) {
bestWindow.focus();
const fileToCheck = filesToOpen[0] || filesToCreate[0] || filesToDiff[0];
const windowOrFolder = findBestWindowOrFolder({
windows: WindowsManager.WINDOWS,
newWindow: openFilesInNewWindow,
reuseWindow: openConfig.forceReuseWindow,
context: openConfig.context,
filePath: fileToCheck && fileToCheck.filePath,
userHome: this.environmentService.userHome
});
if (windowOrFolder instanceof VSCodeWindow) {
windowOrFolder.focus();
const files = { filesToOpen, filesToCreate, filesToDiff }; // copy to object because they get reset shortly after
bestWindow.ready().then(readyWindow => {
windowOrFolder.ready().then(readyWindow => {
readyWindow.send('vscode:openFiles', files);
});
usedWindows.push(bestWindow);
usedWindows.push(windowOrFolder);
}
// Otherwise open instance with files
else {
const configuration = this.toConfiguration(openConfig, null, filesToOpen, filesToCreate, filesToDiff);
const configuration = this.toConfiguration(openConfig, windowOrFolder, filesToOpen, filesToCreate, filesToDiff);
const browserWindow = this.openInBrowserWindow(configuration, true /* new window */);
usedWindows.push(browserWindow);
@@ -530,7 +520,7 @@ export class WindowsManager implements IWindowsMainService {
const mru = this.getRecentPathsList();
paths.forEach(p => {
const {path, isFile} = p;
const { path, isFile } = p;
if (isFile) {
mru.files.unshift(path);
@@ -1026,31 +1016,8 @@ export class WindowsManager implements IWindowsMainService {
return res && res[0];
}
private findBestWindow(context: OpenContext, filePaths: IPath[]): VSCodeWindow {
const findContainer = context === OpenContext.DESKTOP || context === OpenContext.CLI;
return (findContainer && this.findContainingWindow(filePaths)) || this.getLastActiveWindow();
}
private findContainingWindow(filePaths: IPath[]): VSCodeWindow {
for (const filePath of filePaths) {
const windows = WindowsManager.WINDOWS.filter(window => typeof window.openedWorkspacePath === 'string' && paths.isEqualOrParent(filePath.filePath, window.openedWorkspacePath));
if (windows.length) {
return windows.sort((a, b) => -(a.openedWorkspacePath.length - b.openedWorkspacePath.length))[0];
}
}
return null;
}
public getLastActiveWindow(): VSCodeWindow {
if (WindowsManager.WINDOWS.length) {
const lastFocussedDate = Math.max.apply(Math, WindowsManager.WINDOWS.map(w => w.lastFocusTime));
const res = WindowsManager.WINDOWS.filter(w => w.lastFocusTime === lastFocussedDate);
if (res && res.length) {
return res[0];
}
}
return null;
return getLastActiveWindow(WindowsManager.WINDOWS);
}
public findWindow(workspacePath: string, filePath?: string, extensionDevelopmentPath?: string): VSCodeWindow {
+123
View File
@@ -0,0 +1,123 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as path from 'path';
import * as fs from 'original-fs';
import * as platform from 'vs/base/common/platform';
import * as paths from 'vs/base/common/paths';
export enum OpenContext {
// opening when running from the command line
CLI,
// macOS only: opening from the dock (also when opening files to a running instance from desktop)
DOCK,
// opening from the main application window
MENU,
// opening from a file or folder dialog
DIALOG,
// opening from the OS's UI
DESKTOP,
// opening through the API
API
}
/**
* Exported subset of VSCodeWindow for testing.
*/
export interface ISimpleWindow {
openedWorkspacePath: string;
lastFocusTime: number;
}
/**
* Exported for testing.
*/
export interface IBestWindowOrFolderOptions<WINDOW> {
windows: WINDOW[];
newWindow: boolean;
reuseWindow: boolean;
context: OpenContext;
filePath?: string;
userHome?: string;
vscodeFolder?: string;
}
export function findBestWindowOrFolder<WINDOW extends ISimpleWindow>({ windows, newWindow, reuseWindow, context, filePath, userHome, vscodeFolder }: IBestWindowOrFolderOptions<WINDOW>): WINDOW | string {
// OpenContext.DOCK implies newWindow unless overwritten by settings.
const findBest = filePath && (context === OpenContext.DESKTOP || context === OpenContext.CLI || context === OpenContext.DOCK);
const bestWindow = !newWindow && findBest && findBestWindow(windows, filePath);
const bestFolder = !newWindow && !reuseWindow && findBest && findBestFolder(filePath, userHome, vscodeFolder);
if (bestWindow && !(bestFolder && bestFolder.length > bestWindow.openedWorkspacePath.length)) {
return bestWindow;
} else if (bestFolder) {
return bestFolder;
}
return !newWindow ? getLastActiveWindow(windows) : null;
}
function findBestWindow<WINDOW extends ISimpleWindow>(windows: WINDOW[], filePath: string): WINDOW {
const containers = windows.filter(window => typeof window.openedWorkspacePath === 'string' && paths.isEqualOrParent(filePath, window.openedWorkspacePath));
if (containers.length) {
return containers.sort((a, b) => -(a.openedWorkspacePath.length - b.openedWorkspacePath.length))[0];
}
return null;
}
function findBestFolder(filePath: string, userHome?: string, vscodeFolder?: string): string {
let folder = path.dirname(paths.normalize(filePath, true));
let homeFolder = userHome && paths.normalize(userHome, true);
if (!platform.isLinux) {
homeFolder = homeFolder && homeFolder.toLowerCase();
}
let previous = null;
try {
while (folder !== previous) {
if (isProjectFolder(folder, homeFolder, vscodeFolder)) {
return folder;
}
previous = folder;
folder = path.dirname(folder);
}
} catch (err) {
// assume impossible to access
}
return null;
}
function isProjectFolder(folder: string, normalizedUserHome?: string, vscodeFolder = '.vscode') {
try {
if ((platform.isLinux ? folder : folder.toLowerCase()) === normalizedUserHome) {
// ~/.vscode/extensions is used for extensions
return fs.statSync(path.join(folder, vscodeFolder, 'settings.json')).isFile();
} else {
return fs.statSync(path.join(folder, vscodeFolder)).isDirectory();
}
} catch (err) {
if (!(err && err.code === 'ENOENT')) {
throw err;
}
}
return false;
}
export function getLastActiveWindow<WINDOW extends ISimpleWindow>(windows: WINDOW[]): WINDOW {
if (windows.length) {
const lastFocussedDate = Math.max.apply(Math, windows.map(w => w.lastFocusTime));
const res = windows.filter(w => w.lastFocusTime === lastFocussedDate);
if (res && res.length) {
return res[0];
}
}
return null;
}
+154
View File
@@ -0,0 +1,154 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import assert = require('assert');
import path = require('path');
import { findBestWindowOrFolder, OpenContext, ISimpleWindow, IBestWindowOrFolderOptions } from 'vs/code/node/windowsUtils';
const fixturesFolder = require.toUrl('./fixtures');
function options(custom?: Partial<IBestWindowOrFolderOptions<ISimpleWindow>>): IBestWindowOrFolderOptions<ISimpleWindow> {
return {
windows: [],
newWindow: false,
reuseWindow: false,
context: OpenContext.CLI,
vscodeFolder: '_vscode',
...custom
};
}
const vscodeFolderWindow = { lastFocusTime: 1, openedWorkspacePath: path.join(fixturesFolder, 'vscode_folder') };
const lastActiveWindow = { lastFocusTime: 3, openedWorkspacePath: null };
const noVscodeFolderWindow = { lastFocusTime: 2, openedWorkspacePath: path.join(fixturesFolder, 'no_vscode_folder') };
const windows = [
vscodeFolderWindow,
lastActiveWindow,
noVscodeFolderWindow,
];
suite('WindowsUtils', () => {
test('New window without folder when no windows exist', () => {
assert.equal(findBestWindowOrFolder(options()), null);
assert.equal(findBestWindowOrFolder(options({
filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt')
})), null);
assert.equal(findBestWindowOrFolder(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'),
newWindow: true // We assume this implies 'editor' work mode, might need separate CLI option later.
})), null);
assert.equal(findBestWindowOrFolder(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'),
reuseWindow: true // We assume this implies 'editor' work mode, might need separate CLI option later.
})), null);
assert.equal(findBestWindowOrFolder(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'),
context: OpenContext.API
})), null);
});
test('New window with folder when no windows exist', () => {
assert.equal(findBestWindowOrFolder(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt')
})), path.join(fixturesFolder, 'vscode_folder'));
assert.equal(findBestWindowOrFolder(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'new_folder', 'new_file.txt')
})), path.join(fixturesFolder, 'vscode_folder'));
});
test('New window without folder when windows exist', () => {
assert.equal(findBestWindowOrFolder(options({
windows,
filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt'),
newWindow: true
})), null);
});
test('Last active window', () => {
assert.equal(findBestWindowOrFolder(options({
windows
})), lastActiveWindow);
assert.equal(findBestWindowOrFolder(options({
windows,
filePath: path.join(fixturesFolder, 'no_vscode_folder2', 'file.txt')
})), lastActiveWindow);
assert.equal(findBestWindowOrFolder(options({
windows: [lastActiveWindow, noVscodeFolderWindow],
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'),
reuseWindow: true
})), lastActiveWindow);
assert.equal(findBestWindowOrFolder(options({
windows,
filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt'),
context: OpenContext.API
})), lastActiveWindow);
});
test('Existing window with folder', () => {
assert.equal(findBestWindowOrFolder(options({
windows,
filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt')
})), noVscodeFolderWindow);
assert.equal(findBestWindowOrFolder(options({
windows,
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt')
})), vscodeFolderWindow);
});
test('Existing window wins over vscode folder if more specific', () => {
const window = { lastFocusTime: 1, openedWorkspacePath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder') };
assert.equal(findBestWindowOrFolder(options({
windows: [window],
filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder', 'subfolder', 'file.txt')
})), window);
// check
assert.equal(findBestWindowOrFolder(options({
windows: [window],
filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder2', 'subfolder', 'file.txt')
})), path.join(fixturesFolder, 'vscode_folder'));
});
test('More specific existing window wins', () => {
const window = { lastFocusTime: 2, openedWorkspacePath: path.join(fixturesFolder, 'no_vscode_folder') };
const nestedFolderWindow = { lastFocusTime: 1, openedWorkspacePath: path.join(fixturesFolder, 'no_vscode_folder', 'nested_folder') };
assert.equal(findBestWindowOrFolder(options({
windows: [window, nestedFolderWindow],
filePath: path.join(fixturesFolder, 'no_vscode_folder', 'nested_folder', 'subfolder', 'file.txt')
})), nestedFolderWindow);
});
test('VSCode folder wins over existing window if more specific', () => {
const window = { lastFocusTime: 1, openedWorkspacePath: path.join(fixturesFolder, 'vscode_folder') };
assert.equal(findBestWindowOrFolder(options({
windows: [window],
filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_vscode_folder', 'subfolder', 'file.txt')
})), path.join(fixturesFolder, 'vscode_folder', 'nested_vscode_folder'));
// check
assert.equal(findBestWindowOrFolder(options({
windows: [window],
filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder', 'subfolder', 'file.txt')
})), window);
});
test('More specific VSCode folder wins', () => {
assert.equal(findBestWindowOrFolder(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_vscode_folder', 'subfolder', 'file.txt')
})), path.join(fixturesFolder, 'vscode_folder', 'nested_vscode_folder'));
});
test('VSCode folder in home folder needs settings.json', () => {
// Because ~/.vscode/extensions is used for extensions, ~/.vscode is not enough as a hint.
assert.equal(findBestWindowOrFolder(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'),
userHome: path.join(fixturesFolder, 'vscode_folder')
})), null);
assert.equal(findBestWindowOrFolder(options({
filePath: path.join(fixturesFolder, 'vscode_home_folder', 'file.txt'),
userHome: path.join(fixturesFolder, 'vscode_home_folder')
})), path.join(fixturesFolder, 'vscode_home_folder'));
});
});
@@ -29,9 +29,12 @@ class ClipboardEventWrapper implements IClipboardEvent {
return false;
}
public setTextData(text: string): void {
public setTextData(text: string, richText: string): void {
if (this._event.clipboardData) {
this._event.clipboardData.setData('text/plain', text);
if (richText !== null) {
this._event.clipboardData.setData('text/html', richText);
}
this._event.preventDefault();
return;
}
@@ -28,8 +28,7 @@ import { IDisposable, IReference, ImmortalReference, combinedDisposable } from '
import * as dom from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { values } from 'vs/base/common/collections';
import { MenuId, MenuRegistry, ICommandAction, IMenu, IMenuService } from 'vs/platform/actions/common/actions';
import { MenuId, IMenu, IMenuService } from 'vs/platform/actions/common/actions';
import { Menu } from 'vs/platform/actions/common/menu';
import { ITelemetryService, ITelemetryExperiments, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
@@ -428,10 +427,6 @@ export class SimpleMenuService implements IMenuService {
public createMenu(id: MenuId, contextKeyService: IContextKeyService): IMenu {
return new Menu(id, TPromise.as(true), this._commandService, contextKeyService);
}
public getCommandActions(): ICommandAction[] {
return values(MenuRegistry.commands);
}
}
export class StandaloneTelemetryService implements ITelemetryService {
+4
View File
@@ -1006,6 +1006,10 @@ export abstract class CommonCodeEditor extends EventEmitter implements editorCom
protected abstract _registerDecorationType(key: string, options: editorCommon.IDecorationRenderOptions, parentTypeKey?: string): void;
protected abstract _removeDecorationType(key: string): void;
protected abstract _resolveDecorationOptions(typeKey: string, writable: boolean): editorCommon.IModelDecorationOptions;
public getTelemetryData(): { [key: string]: any; } {
return null;
}
}
class EditorContextKeysManager extends Disposable {
@@ -11,8 +11,7 @@ import { KeyCode } from 'vs/base/common/keyCodes';
import { Disposable } from 'vs/base/common/lifecycle';
import { IClipboardEvent, ICompositionEvent, IKeyboardEventWrapper, ISimpleModel, ITextAreaWrapper, ITypeData, TextAreaState, TextAreaStrategy, createTextAreaState } from 'vs/editor/common/controller/textAreaState';
import { Range } from 'vs/editor/common/core/range';
import { Position } from 'vs/editor/common/core/position';
import { EndOfLinePreference } from 'vs/editor/common/editorCommon';
import { InternalEditorOptions } from 'vs/editor/common/editorCommon';
const enum ReadFromTextArea {
Type,
@@ -324,9 +323,13 @@ export class TextAreaHandler extends Disposable {
// ------------- Clipboard operations
private _ensureClipboardGetsEditorSelection(e: IClipboardEvent): void {
let whatToCopy = this._getPlainTextToCopy();
let whatToCopy = this.model.getPlainTextToCopy(this.selections, this.Browser.enableEmptySelectionClipboard);
if (e.canUseTextData()) {
e.setTextData(whatToCopy);
let whatHTMLToCopy = null;
if (!this.Browser.isEdgeOrIE && (whatToCopy.length < 65536 || InternalEditorOptions.forceCopyWithSyntaxHighlighting)) {
whatHTMLToCopy = this.model.getHTMLToCopy(this.selections, this.Browser.enableEmptySelectionClipboard);
}
e.setTextData(whatToCopy, whatHTMLToCopy);
} else {
this.setTextAreaState('copy or cut', this.textAreaState.fromText(whatToCopy), false);
}
@@ -344,31 +347,4 @@ export class TextAreaHandler extends Disposable {
this.lastCopiedValueIsFromEmptySelection = (selections.length === 1 && selections[0].isEmpty());
}
}
private _getPlainTextToCopy(): string {
let newLineCharacter = this.model.getEOL();
let selections = this.selections;
if (selections.length === 1) {
let range: Range = selections[0];
if (range.isEmpty()) {
if (this.Browser.enableEmptySelectionClipboard) {
let modelLineNumber = this.model.coordinatesConverter.convertViewPositionToModelPosition(new Position(range.startLineNumber, 1)).lineNumber;
return this.model.getModelLineContent(modelLineNumber) + newLineCharacter;
} else {
return '';
}
}
return this.model.getValueInRange(range, EndOfLinePreference.TextDefined);
} else {
selections = selections.slice(0).sort(Range.compareRangesUsingStarts);
let result: string[] = [];
for (let i = 0; i < selections.length; i++) {
result.push(this.model.getValueInRange(selections[i], EndOfLinePreference.TextDefined));
}
return result.join(newLineCharacter);
}
}
}
}
@@ -13,7 +13,7 @@ import { Constants } from 'vs/editor/common/core/uint';
export interface IClipboardEvent {
canUseTextData(): boolean;
setTextData(text: string): void;
setTextData(text: string, richText: string): void;
getTextData(): string;
}
@@ -56,6 +56,8 @@ export interface ISimpleModel {
getValueInRange(range: Range, eol: EndOfLinePreference): string;
getModelLineContent(lineNumber: number): string;
getLineCount(): number;
getPlainTextToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string;
getHTMLToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string;
coordinatesConverter: {
convertViewPositionToModelPosition(viewPosition: Position): Position;
+9
View File
@@ -1057,6 +1057,10 @@ export class EditorContribOptions {
* Internal configuration options (transformed or computed) for the editor.
*/
export class InternalEditorOptions {
/**
* @internal
*/
static forceCopyWithSyntaxHighlighting: boolean = false;
readonly _internalEditorOptionsBrand: void;
readonly lineHeight: number; // todo: move to fontInfo
@@ -4122,6 +4126,11 @@ export interface ICommonCodeEditor extends IEditor {
* Get the layout info for the editor.
*/
getLayoutInfo(): EditorLayoutInfo;
/**
* @internal
*/
getTelemetryData(): { [key: string]: any; };
}
export interface ICommonDiffEditor extends IEditor {
@@ -62,12 +62,12 @@ export abstract class EditorAction extends ConfigEditorCommand {
}
public runEditorCommand(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor, args: any): void | TPromise<void> {
this.reportTelemetry(accessor);
this.reportTelemetry(accessor, editor);
return this.run(accessor, editor, args);
}
protected reportTelemetry(accessor: ServicesAccessor) {
accessor.get(ITelemetryService).publicLog('editorActionInvoked', { name: this.label, id: this.id });
protected reportTelemetry(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor) {
accessor.get(ITelemetryService).publicLog('editorActionInvoked', { name: this.label, id: this.id, ...editor.getTelemetryData() });
}
public abstract run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor, args: any): void | TPromise<void>;
@@ -8,11 +8,101 @@ import * as strings from 'vs/base/common/strings';
import { IState, ITokenizationSupport, TokenizationRegistry, LanguageId } from 'vs/editor/common/modes';
import { NULL_STATE, nullTokenize2 } from 'vs/editor/common/modes/nullMode';
import { LineTokens } from 'vs/editor/common/core/lineTokens';
import { CharCode } from 'vs/base/common/charCode';
import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
export function tokenizeToString(text: string, languageId: string): string {
return _tokenizeToString(text, _getSafeTokenizationSupport(languageId));
}
export function tokenizeLineToHTML(text: string, viewLineTokens: ViewLineToken[], rules: { [key: string]: string }, options: { startOffset: number, endOffset: number, tabSize: number }): string {
let tabSize = options.tabSize;
let result = `<div>`;
let charIndex = options.startOffset;
let tabsCharDelta = 0;
for (let tokenIndex = 0, lenJ = viewLineTokens.length; tokenIndex < lenJ; tokenIndex++) {
const token = viewLineTokens[tokenIndex];
const tokenEndIndex = token.endIndex;
if (token.endIndex < options.startOffset) {
continue;
}
const tokenType = token.type;
let partContentCnt = 0;
let partContent = '';
for (; charIndex < tokenEndIndex && charIndex < options.endOffset; charIndex++) {
const charCode = text.charCodeAt(charIndex);
switch (charCode) {
case CharCode.Tab:
let insertSpacesCount = tabSize - (charIndex + tabsCharDelta) % tabSize;
tabsCharDelta += insertSpacesCount - 1;
while (insertSpacesCount > 0) {
partContent += '&nbsp;';
partContentCnt++;
insertSpacesCount--;
}
break;
case CharCode.Space:
partContent += '&nbsp;';
partContentCnt++;
break;
case CharCode.LessThan:
partContent += '&lt;';
partContentCnt++;
break;
case CharCode.GreaterThan:
partContent += '&gt;';
partContentCnt++;
break;
case CharCode.Ampersand:
partContent += '&amp;';
partContentCnt++;
break;
case CharCode.Null:
partContent += '&#00;';
partContentCnt++;
break;
case CharCode.UTF8_BOM:
case CharCode.LINE_SEPARATOR_2028:
partContent += '\ufffd';
partContentCnt++;
break;
case CharCode.CarriageReturn:
// zero width space, because carriage return would introduce a line break
partContent += '&#8203';
partContentCnt++;
break;
default:
partContent += String.fromCharCode(charCode);
partContentCnt++;
}
}
// TODO: adopt new view line tokens.
let style = tokenType.split(' ').map(type => rules[type]).join('');
result += `<span style="${style}">${partContent}</span>`;
if (token.endIndex > options.endOffset) {
break;
}
}
result += `</div>`;
return result;
}
function _getSafeTokenizationSupport(languageId: string): ITokenizationSupport {
let tokenizationSupport = TokenizationRegistry.get(languageId);
if (tokenizationSupport) {
@@ -88,6 +88,9 @@ export interface IViewModel extends IEventEmitter {
getModelLineContent(modelLineNumber: number): string;
getModelLineMaxColumn(modelLineNumber: number): number;
validateModelPosition(modelPosition: IPosition): Position;
getPlainTextToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string;
getHTMLToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string;
}
export class MinimapLinesRenderingData {
@@ -11,6 +11,8 @@ import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { TokenizationRegistry } from 'vs/editor/common/modes';
import { tokenizeLineToHTML } from 'vs/editor/common/modes/textToHtmlTokenizer';
import { ViewModelCursors } from 'vs/editor/common/viewModel/viewModelCursors';
import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations';
import { MinimapLinesRenderingData, ViewLineRenderingData, ViewModelDecoration, IViewModel, ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel';
@@ -566,4 +568,99 @@ export class ViewModel extends EventEmitter implements IViewModel {
public validateModelPosition(position: editorCommon.IPosition): Position {
return this.model.validatePosition(position);
}
public getPlainTextToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string {
let newLineCharacter = this.getEOL();
if (ranges.length === 1) {
let range: Range = ranges[0];
if (range.isEmpty()) {
if (enableEmptySelectionClipboard) {
let modelLineNumber = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(range.startLineNumber, 1)).lineNumber;
return this.getModelLineContent(modelLineNumber) + newLineCharacter;
} else {
return '';
}
}
return this.getValueInRange(range, editorCommon.EndOfLinePreference.TextDefined);
} else {
ranges = ranges.slice(0).sort(Range.compareRangesUsingStarts);
let result: string[] = [];
for (let i = 0; i < ranges.length; i++) {
result.push(this.getValueInRange(ranges[i], editorCommon.EndOfLinePreference.TextDefined));
}
return result.join(newLineCharacter);
}
}
public getHTMLToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string {
// TODO: adopt new view line tokens.
let rules: { [key: string]: string } = {};
let colorMap = TokenizationRegistry.getColorMap();
for (let i = 1, len = colorMap.length; i < len; i++) {
let color = colorMap[i];
if (/^(?:[0-9a-fA-F]{3}){1,2}$/.test(color)) {
color = '#' + color;
}
rules[`mtk${i}`] = `color: ${color};`;
}
rules['mtki'] = 'font-style: italic;';
rules['mtkb'] = 'font-weight: bold;';
rules['mtku'] = 'text-decoration: underline;';
let defaultForegroundColor = /^(?:[0-9a-fA-F]{3}){1,2}$/.test(colorMap[1]) ? '#' + colorMap[1] : colorMap[1];
let defaultBackgroundColor = /^(?:[0-9a-fA-F]{3}){1,2}$/.test(colorMap[2]) ? '#' + colorMap[2] : colorMap[2];
let fontInfo = this.configuration.editor.fontInfo;
let output = `<div style="color: ${defaultForegroundColor}; background-color: ${defaultBackgroundColor};` +
`font-family: ${fontInfo.fontFamily}; font-weight: ${fontInfo.fontWeight}; font-size: ${fontInfo.fontSize}px; line-height: ${fontInfo.lineHeight}px">`;
if (ranges.length === 1) {
let range: Range = ranges[0];
if (range.isEmpty()) {
if (enableEmptySelectionClipboard) {
let modelLineNumber = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(range.startLineNumber, 1)).lineNumber;
let viewLineStart = new Position(range.startLineNumber, 1);
let viewLineEnd = new Position(range.startLineNumber, this.getLineMaxColumn(range.startLineNumber));
let startOffset = this.coordinatesConverter.convertViewPositionToModelPosition(viewLineStart).column - 1;
let endOffset = this.coordinatesConverter.convertViewPositionToModelPosition(viewLineEnd).column - 1;
let viewLineRenderingData = this.getViewLineRenderingData(new Range(viewLineStart.lineNumber, viewLineStart.column, viewLineEnd.lineNumber, viewLineEnd.column), modelLineNumber);
let html = tokenizeLineToHTML(this.getModelLineContent(modelLineNumber),
viewLineRenderingData.tokens,
rules,
{
startOffset: startOffset,
endOffset: endOffset,
tabSize: this.getTabSize()
});
output += `${html}`;
} else {
return '';
}
} else {
for (let i = 0, lineCount = range.endLineNumber - range.startLineNumber; i <= lineCount; i++) {
let viewLineRenderingData = this.getViewLineRenderingData(range, range.startLineNumber + i);
let lineContent = viewLineRenderingData.content;
let startOffset = i === 0 ? range.startColumn - 1 : 0;
let endOffset = i === lineCount ? range.endColumn - 1 : lineContent.length;
let html = tokenizeLineToHTML(lineContent, viewLineRenderingData.tokens, rules,
{
startOffset: startOffset,
endOffset: endOffset,
tabSize: this.getTabSize()
});
output += `${html}`;
}
}
}
output += '</div>';
return output;
}
}
@@ -25,6 +25,14 @@ function conditionalEditorAction(testCommand: string) {
return editorAction;
}
function conditionalCopyWithSyntaxHighlighting() {
if (browser.isEdgeOrIE || !browser.supportsExecCommand('copy')) {
return () => { };
}
return editorAction;
}
abstract class ExecCommandAction extends EditorAction {
private browserCommand: string;
@@ -136,3 +144,36 @@ class ExecCommandPasteAction extends ExecCommandAction {
});
}
}
@conditionalCopyWithSyntaxHighlighting()
class ExecCommandCopyWithSyntaxHighlightingAction extends ExecCommandAction {
constructor() {
super('copy', {
id: 'editor.action.clipboardCopyWithSyntaxHighlightingAction',
label: nls.localize('actions.clipboard.copyWithSyntaxHighlightingLabel', "Copy With Syntax Highlighting"),
alias: 'Copy With Syntax Highlighting',
precondition: null,
kbOpts: {
kbExpr: EditorContextKeys.TextFocus,
primary: null
},
menuOpts: {
group: CLIPBOARD_CONTEXT_MENU_GROUP,
order: 2
}
});
}
public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void {
var enableEmptySelectionClipboard = editor.getConfiguration().contribInfo.emptySelectionClipboard && browser.enableEmptySelectionClipboard;
if (!enableEmptySelectionClipboard && editor.getSelection().isEmpty()) {
return;
}
editorCommon.InternalEditorOptions.forceCopyWithSyntaxHighlighting = true;
super.run(accessor, editor);
editorCommon.InternalEditorOptions.forceCopyWithSyntaxHighlighting = false;
}
}
@@ -505,7 +505,7 @@ abstract class FoldingAction<T> extends EditorAction {
if (!foldingController) {
return;
}
this.reportTelemetry(accessor);
this.reportTelemetry(accessor, editor);
this.invoke(foldingController, editor, args);
}
@@ -3,6 +3,27 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-editor .monaco-editor-overlaymessage {
padding-bottom: 8px;
}
.monaco-editor .monaco-editor-overlaymessage .message {
padding: 1px 4px;
background: #1382CE;
color: white;
}
.monaco-editor .monaco-editor-overlaymessage .anchor {
width: 0 !important;
height: 0 !important;
border-color: transparent;
border-style: solid;
z-index: 1000;
border-width: 8px;
border-top-color: #1382CE;
position: absolute;
}
.monaco-editor .preview-zone-widget.preview .monaco-editor,
.monaco-editor .preview-zone-widget.preview .monaco-editor-background,
.monaco-editor .preview-zone-widget.preview .monaco-editor .line-numbers,
@@ -8,6 +8,7 @@
import 'vs/css!./goToDeclaration';
import * as nls from 'vs/nls';
import { Throttler } from 'vs/base/common/async';
import { any, filterEvent } from 'vs/base/common/event';
import { onUnexpectedError } from 'vs/base/common/errors';
import { MarkedString } from 'vs/base/common/htmlContent';
import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes';
@@ -24,7 +25,7 @@ import { Range } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { editorAction, IActionOptions, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions';
import { Location, DefinitionProviderRegistry } from 'vs/editor/common/modes';
import { ICodeEditor, IEditorMouseEvent, IMouseTarget } from 'vs/editor/browser/editorBrowser';
import { ICodeEditor, IEditorMouseEvent, IMouseTarget, IContentWidget, IContentWidgetPosition, ContentWidgetPositionPreference } from 'vs/editor/browser/editorBrowser';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition } from 'vs/editor/contrib/goToDeclaration/common/goToDeclaration';
import { ReferencesController } from 'vs/editor/contrib/referenceSearch/browser/referencesController';
@@ -34,16 +35,90 @@ import { PeekContext } from 'vs/editor/contrib/zoneWidget/browser/peekViewWidget
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ITextModelResolverService } from 'vs/editor/common/services/resolverService';
import * as corePosition from 'vs/editor/common/core/position';
import ModeContextKeys = editorCommon.ModeContextKeys;
import EditorContextKeys = editorCommon.EditorContextKeys;
class MessageOverlay implements IContentWidget {
private static _last: IDisposable;
static show(editor: ICodeEditor, pos: editorCommon.IPosition, message: string): void {
dispose(MessageOverlay._last);
const widget = new MessageOverlay(message, pos.lineNumber, editor.getOffsetForColumn(pos.lineNumber, pos.column));
const remove = () => editor.removeContentWidget(widget);
editor.addContentWidget(widget);
const listener = any<any>(
filterEvent(editor.onMouseMove, e => {
const { position } = e.target;
return Math.abs(position.lineNumber - pos.lineNumber) > 1
|| Math.abs(position.column - pos.column) > 10;
}),
editor.onDidChangeCursorPosition,
editor.onDidBlurEditorText,
editor.onDidDispose,
editor.onDidChangeModel
)(_ => {
listener.dispose();
remove();
});
MessageOverlay._last = {
dispose() {
listener.dispose();
remove();
}
};
}
readonly allowEditorOverflow = true;
readonly suppressMouseDown = false;
private _position: editorCommon.IPosition;
private _domNode: HTMLDivElement;
constructor(text: string, lineNumber: number, horizontalOffset: number) {
this._position = { lineNumber, column: 1 };
this._domNode = document.createElement('div');
this._domNode.style.paddingLeft = `${horizontalOffset - 6}px`;
this._domNode.classList.add('monaco-editor-overlaymessage');
const message = document.createElement('div');
message.classList.add('message');
message.textContent = text;
this._domNode.appendChild(message);
const anchor = document.createElement('div');
anchor.classList.add('anchor');
this._domNode.appendChild(anchor);
}
getId(): string {
return 'messageoverlay';
}
getDomNode(): HTMLElement {
return this._domNode;
}
getPosition(): IContentWidgetPosition {
return { position: this._position, preference: [ContentWidgetPositionPreference.ABOVE] };
}
}
export class DefinitionActionConfig {
constructor(
public openToSide = false,
public openInPeek = false,
public filterCurrent = true
public filterCurrent = true,
public noResultsMessage = nls.localize('generic.noResults', "Could not find anything")
) {
//
}
@@ -67,10 +142,6 @@ export class DefinitionAction extends EditorAction {
return this.getDeclarationsAtPosition(model, pos).then(references => {
if (!references) {
return;
}
// * remove falsy references
// * remove reference at the current pos
// * collapse ranges to start pos
@@ -80,7 +151,7 @@ export class DefinitionAction extends EditorAction {
if (!reference || !reference.range) {
continue;
}
let {uri, range} = reference;
let { uri, range } = reference;
if (!this._configuration.filterCurrent
|| uri.toString() !== model.uri.toString()
|| !Range.containsPosition(range, pos)) {
@@ -93,6 +164,7 @@ export class DefinitionAction extends EditorAction {
}
if (result.length === 0) {
MessageOverlay.show(<ICodeEditor>editor, pos, this._configuration.noResultsMessage);
return;
}
@@ -125,7 +197,7 @@ export class DefinitionAction extends EditorAction {
}
private _openReference(editorService: IEditorService, reference: Location, sideBySide: boolean): TPromise<editorCommon.ICommonCodeEditor> {
let {uri, range} = reference;
let { uri, range } = reference;
return editorService.openEditor({
resource: uri,
options: {
@@ -322,7 +322,7 @@ class MarkerNavigationAction extends EditorAction {
}
let model = controller.getOrCreateModel();
telemetryService.publicLog('zoneWidgetShown', { mode: 'go to error' });
telemetryService.publicLog('zoneWidgetShown', { mode: 'go to error', ...editor.getTelemetryData() });
if (model) {
if (this._isNext) {
model.next();
@@ -14,7 +14,7 @@ import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import { addClass, append, $, hide, removeClass, show, toggleClass } from 'vs/base/browser/dom';
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
import { IDelegate, IFocusChangeEvent, IRenderer, ISelectionChangeEvent } from 'vs/base/browser/ui/list/list';
import { IDelegate, IListEvent, IRenderer } from 'vs/base/browser/ui/list/list';
import { List } from 'vs/base/browser/ui/list/listWidget';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -407,7 +407,7 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
});
}
private onListSelection(e: ISelectionChangeEvent<ICompletionItem>): void {
private onListSelection(e: IListEvent<ICompletionItem>): void {
if (!e.elements.length) {
return;
}
@@ -439,7 +439,7 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
}
}
private onListFocus(e: IFocusChangeEvent<ICompletionItem>): void {
private onListFocus(e: IListEvent<ICompletionItem>): void {
if (!e.elements.length) {
if (this.currentSuggestionDetails) {
this.currentSuggestionDetails.cancel();
@@ -591,9 +591,9 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
this.completionModel = null;
} else {
const {stats} = this.completionModel;
const { stats } = this.completionModel;
stats['wasAutomaticallyTriggered'] = !!isAuto;
this.telemetryService.publicLog('suggestWidget', stats);
this.telemetryService.publicLog('suggestWidget', { ...stats, ...this.editor.getTelemetryData() });
this.list.splice(0, this.list.length, this.completionModel.items);
this.list.setFocus([this.completionModel.topScoreIdx]);
@@ -694,7 +694,7 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
this.setState(State.Details);
this.editor.focus();
this.telemetryService.publicLog('suggestWidget:toggleDetails');
this.telemetryService.publicLog('suggestWidget:toggleDetails', this.editor.getTelemetryData());
}
private show(): void {
@@ -21,6 +21,7 @@ import { grammarsExtPoint, IEmbeddedLanguagesMap, ITMSyntaxExtensionPoint } from
import { TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token';
import { TokenMetadata } from 'vs/editor/common/model/tokensBinaryEncoding';
import { nullTokenize2 } from 'vs/editor/common/modes/nullMode';
import { hexToCSS } from 'vs/base/common/color';
export class TMScopeRegistry {
@@ -153,7 +154,7 @@ export class MainProcessTextMateSyntax implements ITextMateService {
let rules: string[] = [];
for (let i = 1, len = colorMap.length; i < len; i++) {
let color = colorMap[i];
rules[i] = `.mtk${i} { color: ${color.substr(0, 7)}; }`;
rules[i] = `.mtk${i} { color: ${hexToCSS(color)}; }`;
}
rules.push('.mtki { font-style: italic; }');
rules.push('.mtkb { font-weight: bold; }');
@@ -53,6 +53,14 @@ class SingleLineTestModel implements ISimpleModel {
getLineCount(): number {
return 1;
}
public getPlainTextToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string {
return '';
}
public getHTMLToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string {
return '';
}
}
class TestView {
@@ -180,4 +188,4 @@ const TESTS = [
TESTS.forEach((t) => {
document.body.appendChild(doCreateTest(TextAreaStrategy.NVDA, t.description, t.in, t.out));
document.body.appendChild(doCreateTest(TextAreaStrategy.IENarrator, t.description, t.in, t.out));
});
});
@@ -472,4 +472,12 @@ class SimpleModel implements ISimpleModel {
public getLineCount(): number {
return this._lines.length;
}
public getPlainTextToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string {
return '';
}
public getHTMLToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string {
return '';
}
}
@@ -28,6 +28,7 @@ namespace schema {
export function parseMenuId(value: string): MenuId {
switch (value) {
case 'commandPalette': return MenuId.CommandPalette;
case 'editor/title': return MenuId.EditorTitle;
case 'editor/context': return MenuId.EditorContext;
case 'explorer/context': return MenuId.ExplorerContext;
@@ -224,7 +225,7 @@ ExtensionsRegistry.registerExtensionPoint<schema.IUserFriendlyCommand | schema.I
return;
}
let {icon, category, title, command} = userFriendlyCommand;
let { icon, category, title, command } = userFriendlyCommand;
let iconClass: string;
if (icon) {
iconClass = ids.nextId();
@@ -245,7 +246,7 @@ ExtensionsRegistry.registerExtensionPoint<schema.IUserFriendlyCommand | schema.I
}
for (let extension of extensions) {
const {value} = extension;
const { value } = extension;
if (Array.isArray<schema.IUserFriendlyCommand>(value)) {
for (let command of value) {
handleCommand(command, extension);
@@ -259,7 +260,7 @@ ExtensionsRegistry.registerExtensionPoint<schema.IUserFriendlyCommand | schema.I
ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyMenuItem[] }>('menus', [], schema.menusContribtion).setHandler(extensions => {
for (let extension of extensions) {
const {value, collector} = extension;
const { value, collector } = extension;
forEach(value, entry => {
if (!schema.isValidMenuItems(entry.value, collector)) {
+32 -11
View File
@@ -21,11 +21,6 @@ export interface ICommandAction {
iconClass?: string;
}
export interface IMenu extends IDisposable {
onDidChange: Event<IMenu>;
getActions(arg?: any): [string, MenuItemAction[]][];
}
export interface IMenuItem {
command: ICommandAction;
alt?: ICommandAction;
@@ -49,6 +44,7 @@ export class MenuId {
static readonly SCMTitle = new MenuId('11');
static readonly SCMResourceGroupContext = new MenuId('12');
static readonly SCMResourceContext = new MenuId('13');
static readonly CommandPalette = new MenuId('14');
constructor(private _id: string) {
@@ -59,6 +55,11 @@ export class MenuId {
}
}
export interface IMenu extends IDisposable {
onDidChange: Event<IMenu>;
getActions(arg?: any): [string, MenuItemAction[]][];
}
export const IMenuService = createDecorator<IMenuService>('menuService');
export interface IMenuService {
@@ -66,8 +67,6 @@ export interface IMenuService {
_serviceBrand: any;
createMenu(id: MenuId, scopedKeybindingService: IContextKeyService): IMenu;
getCommandActions(): ICommandAction[];
}
export interface IMenuRegistry {
@@ -94,7 +93,7 @@ export const MenuRegistry: IMenuRegistry = new class {
return this.commands[id];
}
appendMenuItem({id}: MenuId, item: IMenuItem): IDisposable {
appendMenuItem({ id }: MenuId, item: IMenuItem): IDisposable {
let array = this.menuItems[id];
if (!array) {
this.menuItems[id] = array = [item];
@@ -111,8 +110,30 @@ export const MenuRegistry: IMenuRegistry = new class {
};
}
getMenuItems({id}: MenuId): IMenuItem[] {
return this.menuItems[id] || [];
getMenuItems({ id }: MenuId): IMenuItem[] {
const result = this.menuItems[id] || [];
if (id === MenuId.CommandPalette.id) {
// CommandPalette is special because it shows
// all commands by default
this._appendImplicitItems(result);
}
return result;
}
private _appendImplicitItems(result: IMenuItem[]) {
const set = new Set<string>();
for (const { command, alt } of result) {
set.add(command.id);
if (alt) {
set.add(alt.id);
}
}
for (let id in this.commands) {
if (!set.has(id)) {
result.push({ command: this.commands[id] });
}
}
}
};
@@ -202,4 +223,4 @@ export class SyncActionDescriptor {
public get keybindingWeight(): number {
return this._keybindingWeight;
}
}
}
+1 -1
View File
@@ -21,7 +21,7 @@ export class Menu implements IMenu {
private _onDidChange = new Emitter<IMenu>();
constructor(
id: MenuId,
private id: MenuId,
startupSignal: TPromise<boolean>,
@ICommandService private _commandService: ICommandService,
@IContextKeyService private _contextKeyService: IContextKeyService
@@ -5,9 +5,8 @@
'use strict';
import { values } from 'vs/base/common/collections';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { MenuId, MenuRegistry, ICommandAction, IMenu, IMenuService } from 'vs/platform/actions/common/actions';
import { MenuId, IMenu, IMenuService } from 'vs/platform/actions/common/actions';
import { Menu } from 'vs/platform/actions/common/menu';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { ICommandService } from 'vs/platform/commands/common/commands';
@@ -26,8 +25,4 @@ export class MenuService implements IMenuService {
createMenu(id: MenuId, contextKeyService: IContextKeyService): IMenu {
return new Menu(id, this._extensionService.onReady(), this._commandService, contextKeyService);
}
getCommandActions(): ICommandAction[] {
return values(MenuRegistry.commands);
}
}
@@ -186,4 +186,20 @@ suite('MenuService', function () {
assert.equal(two.id, 'b');
assert.equal(three.id, 'a');
});
test('special MenuId palette', function () {
disposables.push(MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: { id: 'a', title: 'Explicit' }
}));
MenuRegistry.addCommand({ id: 'b', title: 'Implicit' });
const [first, second] = MenuRegistry.getMenuItems(MenuId.CommandPalette);
assert.equal(first.command.id, 'a');
assert.equal(first.command.title, 'Explicit');
assert.equal(second.command.id, 'b');
assert.equal(second.command.title, 'Implicit');
});
});
@@ -122,13 +122,13 @@ class ConfigurationRegistry implements IConfigurationRegistry {
this.updateOverridePropertyPatternKey();
}
public registerDefaultConfigurations(defaultConnfigurations: IDefaultConfigurationExtension[]): void {
public registerDefaultConfigurations(defaultConfigurations: IDefaultConfigurationExtension[]): void {
const configurationNode: IConfigurationNode = {
id: 'defaultOverrides',
title: nls.localize('defaultConfigurations.title', "Default Configuration Overrides"),
properties: {}
};
for (const defaultConfiguration of defaultConnfigurations) {
for (const defaultConfiguration of defaultConfigurations) {
for (const key in defaultConfiguration.defaults) {
const defaultValue = defaultConfiguration.defaults[key];
if (OVERRIDE_PROPERTY_PATTERN.test(key) && typeof defaultValue === 'object') {
@@ -141,7 +141,9 @@ class ConfigurationRegistry implements IConfigurationRegistry {
}
}
}
this.registerConfiguration(configurationNode, false);
if (Object.keys(configurationNode.properties).length) {
this.registerConfiguration(configurationNode, false);
}
}
private validateAndRegisterProperties(configuration: IConfigurationNode, validate: boolean = true, overridable: boolean = false) {
@@ -360,10 +362,8 @@ configurationExtPoint.setHandler(extensions => {
validateProperties(configuration, collector);
if (configuration.properties) {
configuration.id = extensions[i].description.id;
configurations.push(configuration);
}
configuration.id = extensions[i].description.id;
configurations.push(configuration);
}
configurationRegistry.registerConfigurations(configurations, false);
@@ -29,25 +29,25 @@ function getUniqueUserId(): string {
return crypto.createHash('sha256').update(username).digest('hex').substr(0, 6);
}
function getIPCHandlePrefix(nixBaseDir: string = os.tmpdir()): string {
let name = pkg.name;
// Support to run VS Code multiple times as different user
// by making the socket unique over the logged in user
let userId = getUniqueUserId();
if (userId) {
name += `-${userId}`;
}
if (process.platform === 'win32') {
return `\\\\.\\pipe\\${name}`;
} else {
return path.join(nixBaseDir, name);
}
function getNixIPCHandle(userDataPath: string, type: string): string {
return path.join(userDataPath, `${pkg.version}-${type}.sock`);
}
function getIPCHandleSuffix(): string {
return process.platform === 'win32' ? '-sock' : '.sock';
function getWin32IPCHandle(type: string): string {
// Support to run VS Code multiple times as different user
// by making the socket unique over the logged in user
const userId = getUniqueUserId();
const name = product.applicationName + (userId ? `-${userId}` : '');
return `\\\\.\\pipe\\${name}-${pkg.version}-${type}-sock`;
}
function getIPCHandle(userDataPath: string, type: string): string {
if (process.platform === 'win32') {
return getWin32IPCHandle(type);
} else {
return getNixIPCHandle(userDataPath, type);
}
}
export class EnvironmentService implements IEnvironmentService {
@@ -113,10 +113,10 @@ export class EnvironmentService implements IEnvironmentService {
get logExtensionHostCommunication(): boolean { return this._args.logExtensionHostCommunication; }
@memoize
get mainIPCHandle(): string { return `${getIPCHandlePrefix(this.userDataPath)}-${pkg.version}${getIPCHandleSuffix()}`; }
get mainIPCHandle(): string { return getIPCHandle(this.userDataPath, 'main'); }
@memoize
get sharedIPCHandle(): string { return `${getIPCHandlePrefix(this.userDataPath)}-${pkg.version}-shared${getIPCHandleSuffix()}`; }
get sharedIPCHandle(): string { return getIPCHandle(this.userDataPath, 'shared'); }
@memoize
get nodeCachedDataDir(): string { return path.join(this.userDataPath, 'CachedData'); }
@@ -15,11 +15,17 @@ export interface ITelemetryInfo {
instanceId: string;
}
export interface ITelemetryData {
from?: string;
target?: string;
[key: string]: any;
}
export interface ITelemetryExperiments {
showNewUserWatermark: boolean;
openUntitledFile: boolean;
openGettingStarted?: boolean;
enableWelcomePage: boolean;
repositionPlaygroundLink: boolean;
}
export interface ITelemetryService {
@@ -30,7 +36,7 @@ export interface ITelemetryService {
* Sends a telemetry event that has been privacy approved.
* Do not call this unless you have been given approval.
*/
publicLog(eventName: string, data?: any): TPromise<void>;
publicLog(eventName: string, data?: ITelemetryData): TPromise<void>;
getTelemetryInfo(): TPromise<ITelemetryInfo>;
@@ -7,7 +7,7 @@
import { localize } from 'vs/nls';
import { escapeRegExpCharacters } from 'vs/base/common/strings';
import { ITelemetryService, ITelemetryInfo, ITelemetryExperiments } from 'vs/platform/telemetry/common/telemetry';
import { ITelemetryService, ITelemetryInfo, ITelemetryExperiments, ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
import { ITelemetryAppender, defaultExperiments } from 'vs/platform/telemetry/common/telemetryUtils';
import { optional } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -100,7 +100,7 @@ export class TelemetryService implements ITelemetryService {
this._disposables = dispose(this._disposables);
}
publicLog(eventName: string, data?: any): TPromise<any> {
publicLog(eventName: string, data?: ITelemetryData): TPromise<any> {
// don't send events when the user is optout
if (!this._userOptIn) {
return TPromise.as(undefined);
@@ -14,7 +14,7 @@ import { IKeybindingService, KeybindingSource } from 'vs/platform/keybinding/com
import { ILifecycleService, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { ITelemetryService, ITelemetryExperiments, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
import { ITelemetryService, ITelemetryExperiments, ITelemetryInfo, ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { StorageService } from 'vs/platform/storage/common/storageService';
import * as objects from 'vs/base/common/objects';
@@ -22,13 +22,14 @@ import * as objects from 'vs/base/common/objects';
export const defaultExperiments: ITelemetryExperiments = {
showNewUserWatermark: false,
openUntitledFile: true,
enableWelcomePage: true
enableWelcomePage: true,
repositionPlaygroundLink: false,
};
export const NullTelemetryService = {
_serviceBrand: undefined,
_experiments: defaultExperiments,
publicLog(eventName: string, data?: any) {
publicLog(eventName: string, data?: ITelemetryData) {
return TPromise.as<void>(null);
},
isOptedIn: true,
@@ -44,9 +45,6 @@ export const NullTelemetryService = {
}
};
const beginGettingStartedExp = Date.UTC(2017, 0, 9);
const endGettingStartedExp = Date.UTC(2017, 0, 16);
export function loadExperiments(accessor: ServicesAccessor): ITelemetryExperiments {
const contextService = accessor.get(IWorkspaceContextService);
const storageService = accessor.get(IStorageService);
@@ -58,8 +56,8 @@ export function loadExperiments(accessor: ServicesAccessor): ITelemetryExperimen
let {
showNewUserWatermark,
openUntitledFile,
openGettingStarted,
enableWelcomePage
enableWelcomePage,
repositionPlaygroundLink,
} = splitExperimentsRandomness();
const newUserDuration = 24 * 60 * 60 * 1000;
@@ -70,17 +68,11 @@ export function loadExperiments(accessor: ServicesAccessor): ITelemetryExperimen
openUntitledFile = defaultExperiments.openUntitledFile;
}
const isNewSession = !storageService.get('telemetry.lastSessionDate');
const now = Date.now();
if (!(isNewSession && now >= beginGettingStartedExp && now < endGettingStartedExp)) {
openGettingStarted = undefined;
}
return applyOverrides({
showNewUserWatermark,
openUntitledFile,
openGettingStarted,
enableWelcomePage
enableWelcomePage,
repositionPlaygroundLink,
});
}
@@ -103,13 +95,13 @@ function splitExperimentsRandomness(): ITelemetryExperiments {
const random1 = getExperimentsRandomness();
const [random2, showNewUserWatermark] = splitRandom(random1);
const [random3, openUntitledFile] = splitRandom(random2);
const [random4, openGettingStarted] = splitRandom(random3);
const [random4, repositionPlaygroundLink] = splitRandom(random3);
const [, enableWelcomePage] = splitRandom(random4);
return {
showNewUserWatermark,
openUntitledFile,
openGettingStarted,
enableWelcomePage
enableWelcomePage,
repositionPlaygroundLink,
};
}
+1
View File
@@ -134,6 +134,7 @@ declare module 'vscode' {
readonly resources: SCMResourceGroup[];
readonly onDidChange: Event<SCMResourceGroup[]>;
readonly count?: number | undefined;
readonly state?: string;
getOriginalResource?(uri: Uri, token: CancellationToken): ProviderResult<Uri>;
open?(resource: SCMResource, token: CancellationToken): ProviderResult<void>;
@@ -183,6 +183,7 @@ export abstract class MainThreadMessageServiceShape {
export abstract class MainThreadOutputServiceShape {
$append(channelId: string, label: string, value: string): TPromise<void> { throw ni(); }
$clear(channelId: string, label: string): TPromise<void> { throw ni(); }
$dispose(channelId: string, label: string): TPromise<void> { throw ni(); }
$reveal(channelId: string, label: string, preserveFocus: boolean): TPromise<void> { throw ni(); }
$close(channelId: string): TPromise<void> { throw ni(); }
}
@@ -258,7 +259,7 @@ export type SCMRawResourceGroup = [string /*id*/, string /*label*/, SCMRawResour
export abstract class MainThreadSCMShape {
$register(id: string, features: SCMProviderFeatures): void { throw ni(); }
$unregister(id: string): void { throw ni(); }
$onChange(id: string, resources: SCMRawResourceGroup[], count: number | undefined): void { throw ni(); }
$onChange(id: string, resources: SCMRawResourceGroup[], count: number | undefined, state: string | undefined): void { throw ni(); }
$setInputBoxValue(value: string): void { throw ni(); }
}
@@ -29,7 +29,7 @@ export class ExtHostOutputChannel implements vscode.OutputChannel {
dispose(): void {
if (!this._disposed) {
this._proxy.$clear(this._id, this._name).then(() => {
this._proxy.$dispose(this._id, this._name).then(() => {
this._disposed = true;
});
}
+1 -1
View File
@@ -189,7 +189,7 @@ export class ExtHostSCM {
return [g.id, g.label, rawResources] as SCMRawResourceGroup;
});
this._proxy.$onChange(providerId, rawResourceGroups, provider.count);
this._proxy.$onChange(providerId, rawResourceGroups, provider.count, provider.state);
});
return new Disposable(() => {
@@ -58,4 +58,9 @@ export class MainThreadOutputService extends MainThreadOutputServiceShape {
return undefined;
}
public $dispose(channelId: string, label: string): TPromise<void> {
this._getChannel(channelId, label).dispose();
return undefined;
}
}
+8 -3
View File
@@ -30,6 +30,9 @@ class MainThreadSCMProvider implements ISCMProvider {
private _count: number | undefined = undefined;
get count(): number | undefined { return this._count; }
private _state: string | undefined = undefined;
get state(): string | undefined { return this._state; }
constructor(
private _id: string,
private proxy: ExtHostSCMShape,
@@ -71,7 +74,7 @@ class MainThreadSCMProvider implements ISCMProvider {
// }
}
$onChange(rawResourceGroups: SCMRawResourceGroup[], count: number | undefined): void {
$onChange(rawResourceGroups: SCMRawResourceGroup[], count: number | undefined, state: string | undefined): void {
this._resources = rawResourceGroups.map(rawGroup => {
const [id, label, rawResources] = rawGroup;
@@ -96,7 +99,9 @@ class MainThreadSCMProvider implements ISCMProvider {
return { id, label, resources };
});
this._count = count;
this._state = state;
this._onDidChange.fire(this.resources);
}
@@ -140,14 +145,14 @@ export class MainThreadSCM extends MainThreadSCMShape {
delete this.providers[id];
}
$onChange(id: string, rawResourceGroups: SCMRawResourceGroup[], count: number | undefined): void {
$onChange(id: string, rawResourceGroups: SCMRawResourceGroup[], count: number | undefined, state: string | undefined): void {
const provider = this.providers[id];
if (!provider) {
return;
}
provider.$onChange(rawResourceGroups, count);
provider.$onChange(rawResourceGroups, count, state);
}
$setInputBoxValue(value: string): void {
+3 -2
View File
@@ -168,14 +168,15 @@ export function triggerAndDisposeAction(instantitationService: IInstantiationSer
return undefined;
}
const from = args && args.from || 'keybinding';
if (telemetryService) {
telemetryService.publicLog('workbenchActionExecuted', { id: actionInstance.id, from: args && args.from || 'keybinding' });
telemetryService.publicLog('workbenchActionExecuted', { id: actionInstance.id, from });
}
// run action when workbench is created
return partService.joinCreation().then(() => {
try {
return TPromise.as(actionInstance.run()).then(() => {
return TPromise.as(actionInstance.run(undefined, { from })).then(() => {
actionInstance.dispose();
}, (err) => {
actionInstance.dispose();
@@ -9,6 +9,12 @@
.monaco-shell.vs-dark {
background-color: #1E1E1E;
}
.monaco-shell.vs {
background-color: #FFFFFF;
}
.monaco-shell.hc-black {
background-color: #000000;
}
</style>
</head>
@@ -16,13 +22,13 @@
<script>
(function() {
try {
var theme = window.localStorage.getItem('storage://global/workbench.theme');
if (theme) {
var baseTheme = theme.split(' ')[0];
if (baseTheme !== 'vs-dark') {
window.document.body.className = 'monaco-shell ' + baseTheme;
window.location.search.substring(1).split('&').forEach(p => {
var kv = p.split('=');
if (kv[0] === 'config' && kv[1]) {
let config = JSON.parse(decodeURIComponent(kv[1]));
window.document.body.className = 'monaco-shell ' + config.baseTheme;
}
}
});
} catch (error) {
console.error(error);
}
@@ -144,16 +144,6 @@ function main() {
webFrame.setZoomLevel(zoomLevel);
}
// Handle high contrast mode
if (configuration.highContrast) {
var themeStorageKey = 'storage://global/workbench.theme';
var hcTheme = 'hc-black vscode-theme-defaults-themes-hc_black-json';
if (window.localStorage.getItem(themeStorageKey) !== hcTheme) {
window.localStorage.setItem(themeStorageKey, hcTheme);
window.document.body.className = 'monaco-shell ' + hcTheme;
}
}
// Load the loader and start loading the workbench
const rootUrl = uriFromPath(configuration.appRoot) + '/out';

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