提交 88c45c4e 编写于 作者: A Alex Dima

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

......@@ -9,6 +9,8 @@
a code editor with what developers need for their core edit-build-debug cycle. Code
provides comprehensive editing and debugging support, an extensibility model, and lightweight integration with existing tools.
VS Code is updated monthly with new features and bug fixes. You can download it for Windows, Mac and Linux on [VS Code's website](https://code.visualstudio.com/Download). To get the latest releases everyday, you can install the [Insiders version of VS Code](https://code.visualstudio.com/insiders). This builds from the master branch and is updated at least daily.
<p align="center">
<img alt="VS Code in action" src="https://cloud.githubusercontent.com/assets/11839736/16642200/6624dde0-43bd-11e6-8595-c81885ba0dc2.png">
</p>
......
......@@ -269,7 +269,7 @@ function packageTask(platform, arch, opts) {
.pipe(util.cleanNodeModule('native-keymap', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['**/*.node']))
.pipe(util.cleanNodeModule('windows-foreground-love', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node']))
.pipe(util.cleanNodeModule('gc-signals', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['**/*.node', 'src/index.js']))
.pipe(util.cleanNodeModule('pty.js', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/**']));
.pipe(util.cleanNodeModule('node-pty', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/**']));
let all = es.merge(
packageJsonStream,
......
// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS:
[{
"name": "js-beautify",
"version": "1.6.4",
"version": "1.6.8",
"license": "MIT",
"repositoryURL": "https://github.com/beautify-web/js-beautify"
},{
......
......@@ -87,9 +87,17 @@
"string",
"null"
],
"default": "a, abbr, acronym, b, bdo, big, br, button, cite, code, dfn, em, i, img, input, kbd, label, map, object, pre, q, samp, select, small, span, strong, sub, sup, textarea, tt, var",
"default": "a, abbr, acronym, b, bdo, big, br, button, cite, code, dfn, em, i, img, input, kbd, label, map, object, q, samp, select, small, span, strong, sub, sup, textarea, tt, var",
"description": "%html.format.unformatted.desc%"
},
"html.format.contentUnformatted": {
"type": [
"string",
"null"
],
"default": "pre",
"description": "%html.format.contentUnformatted.desc%"
},
"html.format.indentInnerHtml": {
"type": "boolean",
"default": false,
......@@ -126,6 +134,13 @@
"default": "head, body, /html",
"description": "%html.format.extraLiners.desc%"
},
"html.format.wrapAttributes": {
"type": "string",
"default": "auto",
"enum": [ "auto", "force", "force-align", "force-expand-multiline" ],
"enumDescriptions": ["%html.format.wrapAttributes.auto%", "%html.format.wrapAttributes.force%", "%html.format.wrapAttributes.forcealign%", "%html.format.wrapAttributes.forcemultiline%"],
"description": "%html.format.wrapAttributes.desc%"
},
"html.suggest.angular1": {
"type": "boolean",
"default": true,
......
......@@ -2,12 +2,18 @@
"html.format.enable.desc": "Enable/disable default HTML formatter (requires restart)",
"html.format.wrapLineLength.desc": "Maximum amount of characters per line (0 = disable).",
"html.format.unformatted.desc": "List of tags, comma separated, that shouldn't be reformatted. 'null' defaults to all tags listed at https://www.w3.org/TR/html5/dom.html#phrasing-content.",
"html.format.contentUnformatted.desc": "List of tags, comma separated, where the content shouldn't be reformatted. 'null' defaults to the 'pre' tag.",
"html.format.indentInnerHtml.desc": "Indent <head> and <body> sections.",
"html.format.preserveNewLines.desc": "Whether existing line breaks before elements should be preserved. Only works before elements, not inside tags or for text.",
"html.format.maxPreserveNewLines.desc": "Maximum number of line breaks to be preserved in one chunk. Use 'null' for unlimited.",
"html.format.indentHandlebars.desc": "Format and indent {{#foo}} and {{/foo}}.",
"html.format.endWithNewline.desc": "End with a newline.",
"html.format.extraLiners.desc": "List of tags, comma separated, that should have an extra newline before them. 'null' defaults to \"head, body, /html\".",
"html.format.wrapAttributes.desc": "Wrap attributes.",
"html.format.wrapAttributes.auto": "Wrap attributes only when line length is exceeded.",
"html.format.wrapAttributes.force": "Wrap each attribute except first.",
"html.format.wrapAttributes.forcealign": "Wrap each attribute except first and keep aligned.",
"html.format.wrapAttributes.forcemultiline": "Wrap each attribute.",
"html.suggest.angular1.desc": "Configures if the built-in HTML language support suggests Angular V1 tags and properties.",
"html.suggest.ionic.desc": "Configures if the built-in HTML language support suggests Ionic tags, properties and values.",
"html.suggest.html5.desc":"Configures if the built-in HTML language support suggests HTML5 tags, properties and values.",
......
......@@ -2,15 +2,20 @@
"name": "vscode-html-languageserver",
"version": "1.0.0",
"dependencies": {
"@types/node": {
"version": "6.0.58",
"from": "@types/node@>=6.0.51 <7.0.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.58.tgz"
},
"vscode-css-languageservice": {
"version": "2.0.0-next.6",
"from": "vscode-css-languageservice@next",
"resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-2.0.0-next.6.tgz"
},
"vscode-html-languageservice": {
"version": "2.0.0-next.3",
"version": "2.0.0-next.5",
"from": "vscode-html-languageservice@next",
"resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-2.0.0-next.3.tgz"
"resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-2.0.0-next.5.tgz"
},
"vscode-jsonrpc": {
"version": "3.0.1-alpha.2",
......
......@@ -9,7 +9,7 @@
},
"dependencies": {
"vscode-css-languageservice": "^2.0.0-next.6",
"vscode-html-languageservice": "^2.0.0-next.3",
"vscode-html-languageservice": "^2.0.0-next.5",
"vscode-languageserver": "3.0.1-alpha.2",
"vscode-nls": "^1.0.7",
"vscode-uri": "^1.0.0"
......
......@@ -27,7 +27,7 @@
"extensions": [
".jsx"
],
"configuration": "./language-configuration.json"
"configuration": "./javascript-language-configuration.json"
},
{
"id": "javascript",
......@@ -47,19 +47,31 @@
"mimetypes": [
"text/javascript"
],
"configuration": "./language-configuration.json"
"configuration": "./javascript-language-configuration.json"
},
{
"id": "jsx-tags",
"configuration": "./tags-language-configuration.json"
}
],
"grammars": [
{
"language": "javascriptreact",
"scopeName": "source.js",
"path": "./syntaxes/JavaScript.tmLanguage.json"
"path": "./syntaxes/JavaScript.tmLanguage.json",
"embeddedLanguages": {
"meta.tag.js": "jsx-tags",
"meta.tag.without-attributes.js": "jsx-tags"
}
},
{
"language": "javascript",
"scopeName": "source.js",
"path": "./syntaxes/JavaScript.tmLanguage.json"
"path": "./syntaxes/JavaScript.tmLanguage.json",
"embeddedLanguages": {
"meta.tag.js": "jsx-tags",
"meta.tag.without-attributes.js": "jsx-tags"
}
},
{
"scopeName": "source.js.regexp",
......
{
"comments": {
"blockComment": [ "{/*", "*/}" ]
},
"brackets": [
["{", "}"],
["[", "]"],
["(", ")"],
["<", ">"]
],
"autoClosingPairs": [
{ "open": "{", "close": "}" },
{ "open": "[", "close": "]" },
{ "open": "(", "close": ")" },
{ "open": "'", "close": "'", "notIn": ["string", "comment"] },
{ "open": "\"", "close": "\"", "notIn": ["string"] },
{ "open": "/**", "close": " */", "notIn": ["string"] }
],
"surroundingPairs": [
["{", "}"],
["[", "]"],
["(", ")"],
["<", ">"],
["'", "'"],
["\"", "\""]
]
}
\ No newline at end of file
......@@ -43,9 +43,9 @@
"resolved": "https://registry.npmjs.org/request-light/-/request-light-0.1.0.tgz"
},
"vscode-json-languageservice": {
"version": "2.0.0-next.8",
"version": "2.0.0-next.9",
"from": "vscode-json-languageservice@next",
"resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-2.0.0-next.8.tgz"
"resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-2.0.0-next.9.tgz"
},
"vscode-jsonrpc": {
"version": "3.0.1-alpha.2",
......
......@@ -9,7 +9,7 @@
},
"dependencies": {
"request-light": "^0.1.0",
"vscode-json-languageservice": "^2.0.0-next.8",
"vscode-json-languageservice": "^2.0.0-next.9",
"vscode-languageserver": "3.0.1-alpha.3",
"vscode-nls": "^1.0.7"
},
......
......@@ -68,7 +68,11 @@
{
"language": "typescriptreact",
"scopeName": "source.tsx",
"path": "./syntaxes/TypeScriptReact.tmLanguage.json"
"path": "./syntaxes/TypeScriptReact.tmLanguage.json",
"embeddedLanguages": {
"meta.tag.tsx": "jsx-tags",
"meta.tag.without-attributes.tsx": "jsx-tags"
}
}
],
"configuration": {
......@@ -99,6 +103,11 @@
"default": true,
"description": "%typescript.check.tscVersion%"
},
"typescript.referencesCodeLens.enabled": {
"type": "boolean",
"default": false,
"description": "%typescript.referencesCodeLens.enabled%"
},
"typescript.tsserver.trace": {
"type": "string",
"enum": [
......
......@@ -24,5 +24,6 @@
"format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": "Defines space handling after opening and before closing JSX expression braces. Requires TypeScript >= 2.0.6.",
"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."
"javascript.validate.enable": "Enable/disable JavaScript validation.",
"typescript.referencesCodeLens.enabled": "Enable/disable the references code lens"
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 { CodeLensProvider, CodeLens, CancellationToken, TextDocument, Range, Uri, Location, Position, workspace, WorkspaceConfiguration } from 'vscode';
import * as Proto from '../protocol';
import * as PConst from '../protocol.const';
import { ITypescriptServiceClient } from '../typescriptService';
import * as nls from 'vscode-nls';
let localize = nls.loadMessageBundle();
class ReferencesCodeLens extends CodeLens {
public document: Uri;
public file: string;
constructor(document: Uri, file: string, range: Range) {
super(range);
this.document = document;
this.file = file;
}
}
export default class TypeScriptReferencesCodeLensProvider implements CodeLensProvider {
private client: ITypescriptServiceClient;
private enabled = false;
constructor(client: ITypescriptServiceClient) {
this.client = client;
}
public updateConfiguration(config: WorkspaceConfiguration): void {
let typeScriptConfig = workspace.getConfiguration('typescript');
this.enabled = typeScriptConfig.get('referencesCodeLens.enabled', false);
}
provideCodeLenses(document: TextDocument, token: CancellationToken): Promise<CodeLens[]> {
if (!this.enabled) {
return Promise.resolve([]);
}
const filepath = this.client.asAbsolutePath(document.uri);
if (!filepath) {
return Promise.resolve([]);
}
return this.client.execute('navtree', { file: filepath }, token).then(response => {
const tree = response.body;
const referenceableSpans: Range[] = [];
if (tree && tree.childItems) {
tree.childItems.forEach(item => this.extractReferenceableSymbols(document, item, referenceableSpans));
}
return Promise.resolve(referenceableSpans.map(span => new ReferencesCodeLens(document.uri, filepath, span)));
});
}
resolveCodeLens(inputCodeLens: CodeLens, token: CancellationToken): Promise<CodeLens> {
const codeLens = inputCodeLens as ReferencesCodeLens;
if (!codeLens.document) {
return Promise.reject<CodeLens>(codeLens);
}
const args: Proto.FileLocationRequestArgs = {
file: codeLens.file,
line: codeLens.range.start.line + 1,
offset: codeLens.range.start.character + 1
};
return this.client.execute('references', args, token).then(response => {
if (response && response.body) {
const referenceCount = Math.max(0, response.body.refs.length - 1);
const locations = response.body.refs.map(reference =>
new Location(Uri.file(reference.file),
new Range(
new Position(reference.start.line - 1, reference.start.offset - 1),
new Position(reference.end.line - 1, reference.end.offset - 1))));
codeLens.command = {
title: referenceCount + ' ' + (referenceCount === 1 ? localize('oneReferenceLabel', 'reference') : localize('manyReferenceLabel', 'references')),
command: 'editor.action.showReferences',
arguments: [codeLens.document, codeLens.range.start, locations]
};
return Promise.resolve(codeLens);
}
return Promise.reject(codeLens);
}).catch(() => {
codeLens.command = {
title: localize('referenceErrorLabel', 'Could not determine references'),
command: ''
};
return Promise.resolve(codeLens);
});
}
private extractReferenceableSymbols(document: TextDocument, item: Proto.NavigationTree, results: Range[]) {
if (!item) {
return;
}
const span = item.spans && item.spans[0];
if (span) {
const range = new Range(
new Position(span.start.line - 1, span.start.offset - 1),
new Position(span.end.line - 1, span.end.offset - 1));
// TODO: TS currently requires the position for 'references 'to be inside of the identifer
// Massage the range to make sure this is the case
const text = document.getText(range);
switch (item.kind) {
case PConst.Kind.const:
case PConst.Kind.let:
case PConst.Kind.variable:
case PConst.Kind.function:
// Only show references for exported variables
if (!item.kindModifiers.match(/\bexport\b/)) {
break;
}
// fallthrough
case PConst.Kind.memberFunction:
case PConst.Kind.memberVariable:
case PConst.Kind.memberGetAccessor:
case PConst.Kind.memberSetAccessor:
case PConst.Kind.constructorImplementation:
case PConst.Kind.class:
case PConst.Kind.interface:
case PConst.Kind.type:
case PConst.Kind.enum:
const identifierMatch = new RegExp(`^(.*?(\\b|\\W))${item.text}`, 'g');
const match = identifierMatch.exec(text);
const start = match ? match.index + match[1].length : 0;
results.push(new Range(
new Position(range.start.line, range.start.character + start),
new Position(range.start.line, range.start.character + start + item.text.length)));
break;
}
}
(item.childItems || []).forEach(item => this.extractReferenceableSymbols(document, item, results));
}
};
\ No newline at end of file
......@@ -35,6 +35,7 @@ import BufferSyncSupport from './features/bufferSyncSupport';
import CompletionItemProvider from './features/completionItemProvider';
import WorkspaceSymbolProvider from './features/workspaceSymbolProvider';
import CodeActionProvider from './features/codeActionProvider';
import ReferenceCodeLensProvider from './features/referencesCodeLensProvider';
import * as BuildStatus from './utils/buildStatus';
import * as ProjectStatus from './utils/projectStatus';
......@@ -107,6 +108,7 @@ class LanguageProvider {
private formattingProvider: FormattingProvider;
private formattingProviderRegistration: Disposable | null;
private typingsStatus: TypingsStatus;
private referenceCodeLensProvider: ReferenceCodeLensProvider;
private _validate: boolean;
......@@ -156,6 +158,12 @@ class LanguageProvider {
this.formattingProviderRegistration = languages.registerDocumentRangeFormattingEditProvider(this.description.modeIds, this.formattingProvider);
}
this.referenceCodeLensProvider = new ReferenceCodeLensProvider(client);
this.referenceCodeLensProvider.updateConfiguration(config);
if (client.apiVersion.has206Features()) {
languages.registerCodeLensProvider(this.description.modeIds, this.referenceCodeLensProvider);
}
this.description.modeIds.forEach(modeId => {
let selector: DocumentFilter = { scheme: 'file', language: modeId };
languages.registerCompletionItemProvider(selector, this.completionItemProvider, '.');
......@@ -171,6 +179,7 @@ class LanguageProvider {
if (client.apiVersion.has213Features()) {
languages.registerCodeActionsProvider(selector, new CodeActionProvider(client, modeId));
}
languages.setLanguageConfiguration(modeId, {
indentationRules: {
// ^(.*\*/)?\s*\}.*$
......@@ -217,6 +226,9 @@ class LanguageProvider {
if (this.completionItemProvider) {
this.completionItemProvider.updateConfiguration(config);
}
if (this.referenceCodeLensProvider) {
this.referenceCodeLensProvider.updateConfiguration(config);
}
if (this.formattingProvider) {
this.formattingProvider.updateConfiguration(config);
if (!this.formattingProvider.isEnabled() && this.formattingProviderRegistration) {
......
......@@ -332,10 +332,10 @@
"from": "process-nextick-args@>=1.0.6 <1.1.0",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz"
},
"pty.js": {
"version": "0.3.0",
"from": "https://github.com/Tyriar/pty.js/tarball/c75c2dcb6dcad83b0cb3ef2ae42d0448fb912642",
"resolved": "https://github.com/Tyriar/pty.js/tarball/c75c2dcb6dcad83b0cb3ef2ae42d0448fb912642",
"node-pty": {
"version": "0.4.1",
"from": "node-pty@0.4.1",
"resolved": "https://registry.npmjs.org/node-pty/-/node-pty-0.4.1.tgz",
"dependencies": {
"extend": {
"version": "1.2.1",
......@@ -430,9 +430,9 @@
"resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.0.tgz"
},
"xterm": {
"version": "2.2.0",
"version": "2.2.3",
"from": "git+https://github.com/Tyriar/xterm.js.git#vscode-release/1.9",
"resolved": "git+https://github.com/Tyriar/xterm.js.git#618e6bbd0a0ebaabc4b06ab10bea89768ded62b5"
"resolved": "git+https://github.com/Tyriar/xterm.js.git#01dd436a56ee2370fa9b5aa5bc2e138b22799eda"
},
"yauzl": {
"version": "2.3.1",
......
......@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
declare module 'pty.js' {
declare module 'node-pty' {
export function fork(file: string, args: string[], options: any): Terminal;
export function spawn(file: string, args: string[], options: any): Terminal;
export function createTerminal(file: string, args: string[], options: any): Terminal;
......
......@@ -1043,9 +1043,6 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions
}
function parseString(isValue: boolean): boolean {
if (_scanner.getToken() !== SyntaxKind.StringLiteral) {
return false;
}
let value = _scanner.getTokenValue();
if (isValue) {
onLiteralValue(value);
......@@ -1088,10 +1085,11 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions
}
function parseProperty(): boolean {
if (!parseString(false)) {
if (_scanner.getToken() !== SyntaxKind.StringLiteral) {
handleError(ParseErrorCode.PropertyNameExpected, [], [SyntaxKind.CloseBraceToken, SyntaxKind.CommaToken]);
return false;
}
parseString(false);
if (_scanner.getToken() === SyntaxKind.ColonToken) {
onSeparator(':');
scanNext(); // consume colon
......@@ -1106,9 +1104,6 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions
}
function parseObject(): boolean {
if (_scanner.getToken() !== SyntaxKind.OpenBraceToken) {
return false;
}
onObjectBegin();
scanNext(); // consume open brace
......@@ -1138,9 +1133,6 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions
}
function parseArray(): boolean {
if (_scanner.getToken() !== SyntaxKind.OpenBracketToken) {
return false;
}
onArrayBegin();
scanNext(); // consume open bracket
......@@ -1170,7 +1162,16 @@ export function visit(text: string, visitor: JSONVisitor, options?: ParseOptions
}
function parseValue(): boolean {
return parseArray() || parseObject() || parseString(true) || parseLiteral();
switch (_scanner.getToken()) {
case SyntaxKind.OpenBracketToken:
return parseArray();
case SyntaxKind.OpenBraceToken:
return parseObject();
case SyntaxKind.StringLiteral:
return parseString(true);
default:
return parseLiteral();
}
}
scanNext();
......
......@@ -261,8 +261,7 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platfo
}
// Install Menu
const menu = instantiationService2.createInstance(VSCodeMenu);
menu.ready();
instantiationService2.createInstance(VSCodeMenu);
});
}
......
......@@ -21,6 +21,10 @@ import { Keybinding } from 'vs/base/common/keyCodes';
import { KeybindingLabels } from 'vs/base/common/keybinding';
import product from 'vs/platform/node/product';
import { RunOnceScheduler } from 'vs/base/common/async';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import Event, { Emitter, once } from 'vs/base/common/event';
import { ConfigWatcher } from 'vs/base/node/config';
import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding';
interface IResolvedKeybinding {
id: string;
......@@ -46,10 +50,97 @@ interface IConfiguration extends IFilesConfiguration {
};
}
export class VSCodeMenu {
class KeybindingsResolver {
private static lastKnownKeybindingsMapStorageKey = 'lastKnownKeybindings';
private commandIds: Set<string>;
private keybindings: { [commandId: string]: string };
private keybindingsWatcher: ConfigWatcher<IUserFriendlyKeybinding[]>;
private _onKeybindingsChanged = new Emitter<void>();
onKeybindingsChanged: Event<void> = this._onKeybindingsChanged.event;
constructor(
@IStorageService private storageService: IStorageService,
@IEnvironmentService environmentService: IEnvironmentService,
@IWindowsMainService private windowsService: IWindowsMainService
) {
this.commandIds = new Set<string>();
this.keybindings = this.storageService.getItem<{ [id: string]: string; }>(KeybindingsResolver.lastKnownKeybindingsMapStorageKey) || Object.create(null);
this.keybindingsWatcher = new ConfigWatcher<IUserFriendlyKeybinding[]>(environmentService.appKeybindingsPath, { changeBufferDelay: 1000 /* update after 1s */ });
this.registerListeners();
}
private registerListeners(): void {
// Resolve keybindings when any first window is loaded
const onceOnWindowReady = once(this.windowsService.onWindowReady);
onceOnWindowReady(win => this.resolveKeybindings(win));
// Listen to resolved keybindings from window
ipc.on('vscode:keybindingsResolved', (event, rawKeybindings: string) => {
let keybindings: IResolvedKeybinding[] = [];
try {
keybindings = JSON.parse(rawKeybindings);
} catch (error) {
// Should not happen
}
// Fill hash map of resolved keybindings and check for changes
let keybindingsChanged = false;
let keybindingsCount = 0;
keybindings.forEach(keybinding => {
const accelerator = KeybindingLabels._toElectronAccelerator(new Keybinding(keybinding.binding));
if (accelerator) {
keybindingsCount++;
if (accelerator !== this.keybindings[keybinding.id]) {
this.keybindings[keybinding.id] = accelerator;
keybindingsChanged = true;
}
}
});
// A keybinding might have been unassigned, so we have to account for that too
if (Object.keys(this.keybindings).length !== keybindingsCount) {
keybindingsChanged = true;
}
if (keybindingsChanged) {
this.storageService.setItem(KeybindingsResolver.lastKnownKeybindingsMapStorageKey, this.keybindings); // keep to restore instantly after restart
this._onKeybindingsChanged.fire();
}
});
// Resolve keybindings again when keybindings.json changes
this.keybindingsWatcher.onDidUpdateConfiguration(() => this.resolveKeybindings());
// Resolve keybindings when window reloads because an installed extension could have an impact
this.windowsService.onWindowReload(() => this.resolveKeybindings());
}
private resolveKeybindings(win: VSCodeWindow = this.windowsService.getLastActiveWindow()): void {
if (this.commandIds.size && win) {
const commandIds = [];
this.commandIds.forEach(id => commandIds.push(id));
win.sendWhenReady('vscode:resolveKeybindings', JSON.stringify(commandIds));
}
}
public getKeybinding(commandId: string): string {
if (!this.commandIds.has(commandId)) {
this.commandIds.add(commandId);
}
return this.keybindings[commandId];
}
}
export class VSCodeMenu {
private static MAX_MENU_RECENT_ENTRIES = 10;
private currentAutoSaveSetting: string;
......@@ -62,35 +153,28 @@ export class VSCodeMenu {
private menuUpdater: RunOnceScheduler;
private actionIdKeybindingRequests: string[];
private mapLastKnownKeybindingToActionId: { [id: string]: string; };
private mapResolvedKeybindingToActionId: { [id: string]: string; };
private keybindingsResolved: boolean;
private keybindingsResolver: KeybindingsResolver;
private extensionViewlets: IExtensionViewlet[];
constructor(
@IStorageService private storageService: IStorageService,
@IUpdateService private updateService: IUpdateService,
@IInstantiationService instantiationService: IInstantiationService,
@IConfigurationService private configurationService: IConfigurationService,
@IWindowsMainService private windowsService: IWindowsMainService,
@IEnvironmentService private environmentService: IEnvironmentService,
@ITelemetryService private telemetryService: ITelemetryService
) {
this.actionIdKeybindingRequests = [];
this.extensionViewlets = [];
this.mapResolvedKeybindingToActionId = Object.create(null);
this.mapLastKnownKeybindingToActionId = this.storageService.getItem<{ [id: string]: string; }>(VSCodeMenu.lastKnownKeybindingsMapStorageKey) || Object.create(null);
this.menuUpdater = new RunOnceScheduler(() => this.doUpdateMenu(), 0);
this.keybindingsResolver = instantiationService.createInstance(KeybindingsResolver);
this.onConfigurationUpdated(this.configurationService.getConfiguration<IConfiguration>());
}
public ready(): void {
this.registerListeners();
this.install();
this.registerListeners();
}
private registerListeners(): void {
......@@ -105,43 +189,6 @@ export class VSCodeMenu {
this.windowsService.onRecentPathsChange(paths => this.updateMenu());
this.windowsService.onWindowClose(_ => this.onClose(this.windowsService.getWindowCount()));
// Resolve keybindings when any first workbench is loaded
this.windowsService.onWindowReady(win => this.resolveKeybindings(win));
// Listen to resolved keybindings
ipc.on('vscode:keybindingsResolved', (event, rawKeybindings) => {
let keybindings: IResolvedKeybinding[] = [];
try {
keybindings = JSON.parse(rawKeybindings);
} catch (error) {
// Should not happen
}
// Fill hash map of resolved keybindings
let needsMenuUpdate = false;
keybindings.forEach(keybinding => {
const accelerator = KeybindingLabels._toElectronAccelerator(new Keybinding(keybinding.binding));
if (accelerator) {
this.mapResolvedKeybindingToActionId[keybinding.id] = accelerator;
if (this.mapLastKnownKeybindingToActionId[keybinding.id] !== accelerator) {
needsMenuUpdate = true; // we only need to update when something changed!
}
}
});
// A keybinding might have been unassigned, so we have to account for that too
if (Object.keys(this.mapLastKnownKeybindingToActionId).length !== Object.keys(this.mapResolvedKeybindingToActionId).length) {
needsMenuUpdate = true;
}
if (needsMenuUpdate) {
this.storageService.setItem(VSCodeMenu.lastKnownKeybindingsMapStorageKey, this.mapResolvedKeybindingToActionId); // keep to restore instantly after restart
this.mapLastKnownKeybindingToActionId = this.mapResolvedKeybindingToActionId; // update our last known map
this.updateMenu();
}
});
// Listen to extension viewlets
ipc.on('vscode:extensionViewlets', (event, rawExtensionViewlets) => {
let extensionViewlets: IExtensionViewlet[] = [];
......@@ -162,6 +209,9 @@ export class VSCodeMenu {
// Listen to update service
this.updateService.onStateChange(() => this.updateMenu());
// Listen to keybindings change
this.keybindingsResolver.onKeybindingsChanged(() => this.updateMenu());
}
private onConfigurationUpdated(config: IConfiguration, handleMenu?: boolean): void {
......@@ -201,19 +251,6 @@ export class VSCodeMenu {
}
}
private resolveKeybindings(win: VSCodeWindow): void {
if (this.keybindingsResolved) {
return; // only resolve once
}
this.keybindingsResolved = true;
// Resolve keybindings when workbench window is up
if (this.actionIdKeybindingRequests.length) {
win.send('vscode:resolveKeybindings', JSON.stringify(this.actionIdKeybindingRequests));
}
}
private updateMenu(): void {
this.menuUpdater.schedule(); // buffer multiple attempts to update the menu
}
......@@ -970,19 +1007,7 @@ export class VSCodeMenu {
private getAccelerator(actionId: string, fallback?: string): string {
if (actionId) {
const resolvedKeybinding = this.mapResolvedKeybindingToActionId[actionId];
if (resolvedKeybinding) {
return resolvedKeybinding; // keybinding is fully resolved
}
if (!this.keybindingsResolved) {
this.actionIdKeybindingRequests.push(actionId); // keybinding needs to be resolved
}
const lastKnownKeybinding = this.mapLastKnownKeybindingToActionId[actionId];
if (lastKnownKeybinding) {
return lastKnownKeybinding; // return the last known keybining (chance of mismatch is very low unless it changed)
}
return this.keybindingsResolver.getKeybinding(actionId);
}
return fallback;
......
......@@ -8,6 +8,7 @@
import * as path from 'path';
import * as platform from 'vs/base/common/platform';
import * as objects from 'vs/base/common/objects';
import nls = require('vs/nls');
import { IStorageService } from 'vs/code/electron-main/storage';
import { shell, screen, BrowserWindow, systemPreferences, app } from 'electron';
import { TPromise, TValueCallback } from 'vs/base/common/winjs.base';
......@@ -117,6 +118,12 @@ export enum ReadyState {
READY
}
interface IConfiguration {
window: {
menuBarVisibility: 'visible' | 'toggle' | 'hidden';
};
}
export interface IVSCodeWindow {
id: number;
readyState: ReadyState;
......@@ -127,7 +134,6 @@ export interface IVSCodeWindow {
export class VSCodeWindow implements IVSCodeWindow {
public static menuBarHiddenKey = 'menuBarHidden';
public static colorThemeStorageKey = 'theme';
private static MIN_WIDTH = 200;
......@@ -143,6 +149,7 @@ export class VSCodeWindow implements IVSCodeWindow {
private _extensionDevelopmentPath: string;
private _isExtensionTestHost: boolean;
private windowState: IWindowState;
private currentMenuBarVisibility: 'visible' | 'toggle' | 'hidden';
private currentWindowMode: WindowMode;
private whenReadyCallbacks: TValueCallback<VSCodeWindow>[];
......@@ -220,15 +227,14 @@ export class VSCodeWindow implements IVSCodeWindow {
this._lastFocusTime = Date.now(); // since we show directly, we need to set the last focus time too
if (this.storageService.getItem<boolean>(VSCodeWindow.menuBarHiddenKey, false)) {
this.setMenuBarVisibility(false); // respect configured menu bar visibility
}
// respect configured menu bar visibility
this.onConfigurationUpdated(this.configurationService.getConfiguration<IConfiguration>());
// TODO@joao: hook this up to some initialization routine
// this causes a race between setting the headers and doing
// TODO@joao: hook this up to some initialization routine this causes a race between setting the headers and doing
// a request that needs them. chances are low
this.setCommonHTTPHeaders();
// Eventing
this.registerListeners();
}
......@@ -407,8 +413,19 @@ export class VSCodeWindow implements IVSCodeWindow {
}
});
}
// Handle configuration changes
this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(e.config));
}
private onConfigurationUpdated(config: IConfiguration): void {
const newMenuBarVisibility = this.getMenuBarVisibility(config);
if (newMenuBarVisibility !== this.currentMenuBarVisibility) {
this.currentMenuBarVisibility = newMenuBarVisibility;
this.setMenuBarVisibility(newMenuBarVisibility);
}
};
public load(config: IWindowConfiguration): void {
// If this is the first time the window is loaded, we associate the paths
......@@ -656,21 +673,53 @@ export class VSCodeWindow implements IVSCodeWindow {
public toggleFullScreen(): void {
const willBeFullScreen = !this.win.isFullScreen();
// set fullscreen flag on window
this.win.setFullScreen(willBeFullScreen);
// Windows & Linux: Hide the menu bar but still allow to bring it up by pressing the Alt key
if (platform.isWindows || platform.isLinux) {
if (willBeFullScreen) {
this.setMenuBarVisibility(false);
} else {
this.setMenuBarVisibility(!this.storageService.getItem<boolean>(VSCodeWindow.menuBarHiddenKey, false)); // restore as configured
}
// respect configured menu bar visibility or default to toggle if not set
this.setMenuBarVisibility(this.getMenuBarVisibility(this.configurationService.getConfiguration<IConfiguration>(), willBeFullScreen ? 'toggle' : 'visible'), false);
}
private getMenuBarVisibility(configuration: IConfiguration, fallback: 'visible' | 'toggle' | 'hidden' = 'visible'): 'visible' | 'toggle' | 'hidden' {
const windowConfig = this.configurationService.getConfiguration<IWindowSettings>('window');
if (!windowConfig || !windowConfig.menuBarVisibility) {
return fallback;
}
let menuBarVisibility = windowConfig.menuBarVisibility;
if (['visible', 'toggle', 'hidden'].indexOf(menuBarVisibility) < 0) {
menuBarVisibility = fallback;
}
return menuBarVisibility;
}
public setMenuBarVisibility(visible: boolean): void {
this.win.setMenuBarVisibility(visible);
this.win.setAutoHideMenuBar(!visible);
public setMenuBarVisibility(visibility: 'visible' | 'toggle' | 'hidden', notify: boolean = true): void {
if (platform.isMacintosh) {
return; // ignore for macOS platform
}
switch (visibility) {
case ('visible'):
this.win.setMenuBarVisibility(true);
this.win.setAutoHideMenuBar(false);
break;
case ('toggle'):
this.win.setMenuBarVisibility(false);
this.win.setAutoHideMenuBar(true);
if (notify) {
this.send('vscode:showInfoMessage', nls.localize('hiddenMenuBar', "You can still access the menu bar by pressing the **Alt** key."));
};
break;
case ('hidden'):
this.win.setMenuBarVisibility(false);
this.win.setAutoHideMenuBar(false);
break;
};
}
public sendWhenReady(channel: string, ...args: any[]): void {
......
......@@ -104,6 +104,7 @@ export interface IWindowsMainService {
// events
onWindowReady: CommonEvent<VSCodeWindow>;
onWindowClose: CommonEvent<number>;
onWindowReload: CommonEvent<number>;
onPathsOpen: CommonEvent<IPath[]>;
onRecentPathsChange: CommonEvent<void>;
......@@ -131,7 +132,6 @@ export interface IWindowsMainService {
removeFromRecentPathsList(path: string): void;
removeFromRecentPathsList(paths: string[]): void;
clearRecentPathsList(): void;
toggleMenuBar(windowId: number): void;
quit(): void;
}
......@@ -159,6 +159,9 @@ export class WindowsManager implements IWindowsMainService {
private _onWindowClose = new Emitter<number>();
onWindowClose: CommonEvent<number> = this._onWindowClose.event;
private _onWindowReload = new Emitter<number>();
onWindowReload: CommonEvent<number> = this._onWindowReload.event;
private _onPathsOpen = new Emitter<IPath[]>();
onPathsOpen: CommonEvent<IPath> = this._onPathsOpen.event;
......@@ -292,6 +295,9 @@ export class WindowsManager implements IWindowsMainService {
this.lifecycleService.unload(win, UnloadReason.RELOAD).done(veto => {
if (!veto) {
win.reload(cli);
// Emit
this._onWindowReload.fire(win.id);
}
});
}
......@@ -1176,24 +1182,6 @@ export class WindowsManager implements IWindowsMainService {
return pathA === pathB;
}
public toggleMenuBar(windowId: number): void {
// Update in settings
const menuBarHidden = this.storageService.getItem(VSCodeWindow.menuBarHiddenKey, false);
const newMenuBarHidden = !menuBarHidden;
this.storageService.setItem(VSCodeWindow.menuBarHiddenKey, newMenuBarHidden);
// Update across windows
WindowsManager.WINDOWS.forEach(w => w.setMenuBarVisibility(!newMenuBarHidden));
// Inform user if menu bar is now hidden
if (newMenuBarHidden) {
const vscodeWindow = this.getWindowById(windowId);
if (vscodeWindow) {
vscodeWindow.send('vscode:showInfoMessage', nls.localize('hiddenMenuBar', "You can still access the menu bar by pressing the **Alt** key."));
}
}
}
private updateWindowsJumpList(): void {
if (!platform.isWindows) {
return; // only on windows
......
......@@ -54,38 +54,69 @@ export function ensureValidWordDefinition(wordDefinition?: RegExp): RegExp {
return result;
}
export function getWordAtText(column: number, wordDefinition: RegExp, text: string, textOffset: number): IWordAtPosition {
function getWordAtPosFast(column: number, wordDefinition: RegExp, text: string, textOffset: number): IWordAtPosition {
// find whitespace enclosed text around column and match from there
// console.log('_getWordAtText: ', column, text, textOffset);
var words = text.match(wordDefinition),
k: number,
startWord: number,
endWord: number,
startColumn: number,
endColumn: number,
word: string;
if (words) {
for (k = 0; k < words.length; k++) {
word = words[k].trim();
if (word.length > 0) {
startWord = text.indexOf(word, endWord);
endWord = startWord + word.length;
startColumn = textOffset + startWord + 1;
endColumn = textOffset + endWord + 1;
if (startColumn <= column && column <= endColumn) {
return {
word: word,
startColumn: startColumn,
endColumn: endColumn
};
}
}
if (wordDefinition.test(' ')) {
return getWordAtPosSlow(column, wordDefinition, text, textOffset);
}
let pos = column - 1 - textOffset;
let start = text.lastIndexOf(' ', pos - 1) + 1;
let end = text.indexOf(' ', pos);
if (end === -1) {
end = text.length;
}
wordDefinition.lastIndex = start;
let match: RegExpMatchArray;
while (match = wordDefinition.exec(text)) {
if (match.index <= pos && wordDefinition.lastIndex >= pos) {
return {
word: match[0],
startColumn: textOffset + 1 + match.index,
endColumn: textOffset + 1 + wordDefinition.lastIndex
};
}
}
return null;
}
function getWordAtPosSlow(column: number, wordDefinition: RegExp, text: string, textOffset: number): IWordAtPosition {
// matches all words starting at the beginning
// of the input until it finds a match that encloses
// the desired column. slow but correct
let pos = column - 1 - textOffset;
wordDefinition.lastIndex = 0;
let match: RegExpMatchArray;
while (match = wordDefinition.exec(text)) {
if (match.index > pos) {
// |nW -> matched only after the pos
return null;
} else if (wordDefinition.lastIndex >= pos) {
// W|W -> match encloses pos
return {
word: match[0],
startColumn: textOffset + 1 + match.index,
endColumn: textOffset + 1 + wordDefinition.lastIndex
};
}
}
return null;
}
export function getWordAtText(column: number, wordDefinition: RegExp, text: string, textOffset: number): IWordAtPosition {
const result = getWordAtPosFast(column, wordDefinition, text, textOffset);
// both (getWordAtPosFast and getWordAtPosSlow) leave the wordDefinition-RegExp
// in an undefined state and to not confuse other users of the wordDefinition
// we reset the lastIndex
wordDefinition.lastIndex = 0;
return result;
}
......@@ -148,8 +148,11 @@ class MirrorModel extends MirrorModel2 implements ICommonModel {
// TODO@Joh, TODO@Alex - remove these and make sure the super-things work
private _wordenize(content: string, wordDefinition: RegExp): editorCommon.IWordRange[] {
var result: editorCommon.IWordRange[] = [];
var match: RegExpExecArray;
const result: editorCommon.IWordRange[] = [];
let match: RegExpExecArray;
wordDefinition.lastIndex = 0; // reset lastIndex just to be sure
while (match = wordDefinition.exec(content)) {
if (match[0].length === 0) {
// it did match the empty string
......
......@@ -22,7 +22,7 @@ interface ISnippetPick extends IPickOpenEntry {
class Args {
static fromUser(arg: any): Args {
if (typeof arg !== 'object') {
if (!arg || typeof arg !== 'object') {
return Args._empty;
}
let {snippet, name, langId} = arg;
......
......@@ -62,12 +62,10 @@ export interface ProblemPattern {
loop?: boolean;
mostSignifikant?: boolean;
[key: string]: any;
}
export let problemPatternProperties = ['file', 'message', 'location', 'line', 'column', 'endLine', 'endColumn', 'code', 'severity', 'loop', 'mostSignifikant'];
export let problemPatternProperties = ['file', 'message', 'location', 'line', 'column', 'endLine', 'endColumn', 'code', 'severity', 'loop'];
export interface WatchingPattern {
regexp: RegExp;
......
......@@ -37,7 +37,6 @@ export interface IWindowsService {
maximizeWindow(windowId: number): TPromise<void>;
unmaximizeWindow(windowId: number): TPromise<void>;
setDocumentEdited(windowId: number, flag: boolean): TPromise<void>;
toggleMenuBar(windowId: number): TPromise<void>;
quit(): TPromise<void>;
// Global methods
......@@ -80,7 +79,6 @@ export interface IWindowService {
getRecentlyOpen(): TPromise<{ files: string[]; folders: string[]; }>;
focusWindow(): TPromise<void>;
setDocumentEdited(flag: boolean): TPromise<void>;
toggleMenuBar(): TPromise<void>;
isMaximized(): TPromise<boolean>;
maximizeWindow(): TPromise<void>;
unmaximizeWindow(): TPromise<void>;
......@@ -94,4 +92,5 @@ export interface IWindowSettings {
zoomLevel: number;
titleBarStyle: 'native' | 'custom';
autoDetectHighContrast: boolean;
menuBarVisibility: 'visible' | 'toggle' | 'hidden';
}
......@@ -29,7 +29,6 @@ export interface IWindowsChannel extends IChannel {
call(command: 'maximizeWindow', arg: number): TPromise<void>;
call(command: 'unmaximizeWindow', arg: number): TPromise<void>;
call(command: 'setDocumentEdited', arg: [number, boolean]): TPromise<void>;
call(command: 'toggleMenuBar', arg: number): TPromise<void>;
call(command: 'quit'): TPromise<void>;
call(command: 'openWindow', arg: [string[], { forceNewWindow?: boolean, forceReuseWindow?: boolean }]): TPromise<void>;
call(command: 'openNewWindow'): TPromise<void>;
......@@ -75,7 +74,6 @@ export class WindowsChannel implements IWindowsChannel {
case 'maximizeWindow': return this.service.maximizeWindow(arg);
case 'unmaximizeWindow': return this.service.unmaximizeWindow(arg);
case 'setDocumentEdited': return this.service.setDocumentEdited(arg[0], arg[1]);
case 'toggleMenuBar': return this.service.toggleMenuBar(arg);
case 'openWindow': return this.service.openWindow(arg[0], arg[1]);
case 'openNewWindow': return this.service.openNewWindow();
case 'showWindow': return this.service.showWindow(arg);
......@@ -171,10 +169,6 @@ export class WindowsChannelClient implements IWindowsService {
return this.channel.call('setDocumentEdited', [windowId, flag]);
}
toggleMenuBar(windowId: number): TPromise<void> {
return this.channel.call('toggleMenuBar', windowId);
}
quit(): TPromise<void> {
return this.channel.call('quit');
}
......@@ -218,4 +212,4 @@ export class WindowsChannelClient implements IWindowsService {
startCrashReporter(config: Electron.CrashReporterStartOptions): TPromise<void> {
return this.channel.call('startCrashReporter', config);
}
}
\ No newline at end of file
}
......@@ -89,7 +89,4 @@ export class WindowService implements IWindowService {
return this.windowsService.setDocumentEdited(this.windowId, flag);
}
toggleMenuBar(): TPromise<void> {
return this.windowsService.toggleMenuBar(this.windowId);
}
}
\ No newline at end of file
}
......@@ -193,11 +193,6 @@ export class WindowsService implements IWindowsService, IDisposable {
return TPromise.as(null);
}
toggleMenuBar(windowId: number): TPromise<void> {
this.windowsMainService.toggleMenuBar(windowId);
return TPromise.as(null);
}
openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean }): TPromise<void> {
if (!paths || !paths.length) {
return TPromise.as(null);
......@@ -278,4 +273,4 @@ export class WindowsService implements IWindowsService, IDisposable {
dispose(): void {
this.disposables = dispose(this.disposables);
}
}
\ No newline at end of file
}
......@@ -19,6 +19,7 @@ import { IMessageService, Severity } from 'vs/platform/message/common/message';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IExtensionManagementService, LocalExtensionType, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
......@@ -169,12 +170,36 @@ export class ToggleMenuBarAction extends Action {
static ID = 'workbench.action.toggleMenuBar';
static LABEL = nls.localize('toggleMenuBar', "Toggle Menu Bar");
constructor(id: string, label: string, @IWindowService private windowService: IWindowService) {
private static menuBarVisibilityKey = 'window.menuBarVisibility';
constructor(
id: string,
label: string,
@IMessageService private messageService: IMessageService,
@IConfigurationService private configurationService: IConfigurationService,
@IConfigurationEditingService private configurationEditingService: IConfigurationEditingService
) {
super(id, label);
}
run(): TPromise<void> {
return this.windowService.toggleMenuBar();
public run(): TPromise<any> {
let currentVisibilityValue = this.configurationService.lookup<'visible' | 'toggle' | 'hidden'>(ToggleMenuBarAction.menuBarVisibilityKey).value;
if (typeof (currentVisibilityValue) !== 'string') {
currentVisibilityValue = 'visible';
}
let newVisibilityValue: string;
if (currentVisibilityValue === 'visible') {
newVisibilityValue = 'toggle';
} else {
newVisibilityValue = 'visible';
}
this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: ToggleMenuBarAction.menuBarVisibilityKey, value: newVisibilityValue }).then(null, error => {
this.messageService.show(Severity.Error, error);
});
return TPromise.as(null);
}
}
......
......@@ -225,6 +225,15 @@ Note that there can still be cases where this setting is ignored (e.g. when usin
}
};
if (platform.isWindows || platform.isLinux) {
properties['window.menuBarVisibility'] = {
'type': 'string',
'enum': ['visible', 'toggle', 'hidden'],
'default': 'visible',
'description': nls.localize('menuBarVisibility', "Control the visibility of the menu bar. A setting of 'toggle' means that a single press of the alt key will show and hide the menu bar.")
};
}
if (platform.isWindows) {
properties['window.autoDetectHighContrast'] = {
'type': 'boolean',
......@@ -273,4 +282,4 @@ configurationRegistry.registerConfiguration({
'description': nls.localize('zenMode.hideStatusBar', "Controls if turning on Zen Mode also hides the status bar at the bottom of the workbench.")
}
}
});
});
\ No newline at end of file
......@@ -84,15 +84,11 @@ class ExtensionManifestParser extends ExtensionManifestHandler {
public parse(): TPromise<IExtensionDescription> {
return pfs.readFile(this._absoluteManifestPath).then((manifestContents) => {
let errors: json.ParseError[] = [];
const extensionDescription = json.parse(manifestContents.toString(), errors);
if (errors.length > 0) {
errors.forEach((error) => {
this._collector.error(this._absoluteFolderPath, nls.localize('jsonParseFail', "Failed to parse {0}: {1}.", this._absoluteManifestPath, json.getParseErrorMessage(error.error)));
});
return null;
try {
return JSON.parse(manifestContents.toString());
} catch (e) {
this._collector.error(this._absoluteFolderPath, nls.localize('jsonParseFail', "Failed to parse {0}: {1}.", this._absoluteManifestPath, json.getParseErrorMessage(e.message)));
}
return extensionDescription;
}, (err) => {
if (err.code === 'ENOENT') {
return null;
......@@ -165,40 +161,44 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
/**
* This routine make the following assumptions:
* The root element is a object literal
* Strings to replace are one values of a key. So for example string[] are ignored.
* This is done to speed things up.
*/
private static _replaceNLStrings<T>(literal: T, messages: { [key: string]: string; }, collector: MessagesCollector, messageScope: string): void {
Object.keys(literal).forEach(key => {
if (literal.hasOwnProperty(key)) {
let value = literal[key];
if (Types.isString(value)) {
let str = <string>value;
let length = str.length;
if (length > 1 && str[0] === '%' && str[length - 1] === '%') {
let messageKey = str.substr(1, length - 2);
let message = messages[messageKey];
if (message) {
if (nlsConfig.pseudo) {
// FF3B and FF3D is the Unicode zenkaku representation for [ and ]
message = '\uFF3B' + message.replace(/[aouei]/g, '$&$&') + '\uFF3D';
}
literal[key] = message;
} else {
collector.warn(messageScope, nls.localize('missingNLSKey', "Couldn't find message for key {0}.", messageKey));
function processEntry(obj: any, key: string | number) {
let value = obj[key];
if (Types.isString(value)) {
let str = <string>value;
let length = str.length;
if (length > 1 && str[0] === '%' && str[length - 1] === '%') {
let messageKey = str.substr(1, length - 2);
let message = messages[messageKey];
if (message) {
if (nlsConfig.pseudo) {
// FF3B and FF3D is the Unicode zenkaku representation for [ and ]
message = '\uFF3B' + message.replace(/[aouei]/g, '$&$&') + '\uFF3D';
}
obj[key] = message;
} else {
collector.warn(messageScope, nls.localize('missingNLSKey', "Couldn't find message for key {0}.", messageKey));
}
} else if (Types.isObject(value)) {
ExtensionManifestNLSReplacer._replaceNLStrings(value, messages, collector, messageScope);
} else if (Types.isArray(value)) {
(<any[]>value).forEach(element => {
if (Types.isObject(element)) {
ExtensionManifestNLSReplacer._replaceNLStrings(element, messages, collector, messageScope);
}
});
}
} else if (Types.isObject(value)) {
for (let k in value) {
if (value.hasOwnProperty(k)) {
processEntry(value, k);
}
}
} else if (Types.isArray(value)) {
for (let i = 0; i < value.length; i++) {
processEntry(value, i);
}
}
});
}
for (let key in literal) {
if (literal.hasOwnProperty(key)) {
processEntry(literal, key);
}
};
}
}
......
......@@ -520,7 +520,7 @@ export class Process implements debug.IProcess {
thread.stopped = true;
thread.clearCallStack();
});
} else {
} else if (this.threads.has(data.threadId)) {
// One thread is stopped, only update that thread.
const thread = this.threads.get(data.threadId);
thread.stoppedDetails = data.stoppedDetails;
......
......@@ -580,11 +580,10 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
.then(null, onUnexpectedError);
}
}
}
if (extension.gallery) {
// Report telemetry only for gallery extensions
this.reportTelemetry(installing, !error);
if (extension.gallery) {
// Report telemetry only for gallery extensions
this.reportTelemetry(installing, !error);
}
}
this._onChange.fire();
}
......
......@@ -46,6 +46,8 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace
import { Keybinding, KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { getCodeEditor } from 'vs/editor/common/services/codeEditorService';
import { IEditorViewState } from 'vs/editor/common/editorCommon';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { ITextModelResolverService } from 'vs/editor/common/services/resolverService';
export interface IEditableData {
action: IAction;
......@@ -271,7 +273,9 @@ class RenameFileAction extends BaseRenameAction {
element: FileStat,
@IFileService fileService: IFileService,
@IMessageService messageService: IMessageService,
@ITextFileService textFileService: ITextFileService
@ITextFileService textFileService: ITextFileService,
@ITextModelResolverService private textModelResolverService: ITextModelResolverService,
@IBackupFileService private backupFileService: IBackupFileService
) {
super(RenameFileAction.ID, nls.localize('rename', "Rename"), element, fileService, messageService, textFileService);
......@@ -280,40 +284,44 @@ class RenameFileAction extends BaseRenameAction {
public runAction(newName: string): TPromise<any> {
// Handle dirty
let revertPromise: TPromise<any> = TPromise.as(null);
// 1. check for dirty files that are being moved and backup to new target
const dirty = this.textFileService.getDirty().filter(d => paths.isEqualOrParent(d.fsPath, this.element.resource.fsPath));
if (dirty.length) {
let message: string;
if (this.element.isDirectory) {
if (dirty.length === 1) {
message = nls.localize('dirtyMessageFolderOne', "You are renaming a folder with unsaved changes in 1 file. Do you want to continue?");
} else {
message = nls.localize('dirtyMessageFolder', "You are renaming a folder with unsaved changes in {0} files. Do you want to continue?", dirty.length);
}
} else {
message = nls.localize('dirtyMessageFile', "You are renaming a file with unsaved changes. Do you want to continue?");
const dirtyRenamed: URI[] = [];
return TPromise.join(dirty.map(d => {
const targetPath = paths.join(this.element.parent.resource.fsPath, newName);
let renamed: URI;
// If the dirty file itself got moved, just reparent it to the target folder
if (this.element.resource.fsPath === d.fsPath) {
renamed = URI.file(targetPath);
}
const res = this.messageService.confirm({
message,
type: 'warning',
detail: nls.localize('dirtyWarning', "Your changes will be lost if you don't save them."),
primaryButton: nls.localize({ key: 'renameLabel', comment: ['&& denotes a mnemonic'] }, "&&Rename")
});
if (!res) {
return TPromise.as(null);
// Otherwise, a parent of the dirty resource got moved, so we have to reparent more complicated. Example:
else {
renamed = URI.file(paths.join(targetPath, d.fsPath.substr(this.element.resource.fsPath.length + 1)));
}
revertPromise = this.textFileService.revertAll(dirty);
}
dirtyRenamed.push(renamed);
return revertPromise.then(() => {
return this.fileService.rename(this.element.resource, newName).then(null, (error: Error) => {
this.onErrorWithRetry(error, () => this.runAction(newName));
const model = this.textFileService.models.get(d);
return this.backupFileService.backupResource(renamed, model.getValue(), model.getVersionId());
}))
// 2. soft revert all dirty since we have backed up their contents
.then(() => this.textFileService.revertAll(dirty, { soft: true /* do not attempt to load content from disk */ }))
// 3.) run the rename operation
.then(() => this.fileService.rename(this.element.resource, newName).then(null, (error: Error) => {
return TPromise.join(dirtyRenamed.map(d => this.backupFileService.discardResourceBackup(d))).then(() => {
this.onErrorWithRetry(error, () => this.runAction(newName));
});
}))
// 4.) resolve those that were dirty to load their previous dirty contents from disk
.then(() => {
return TPromise.join(dirtyRenamed.map(t => this.textModelResolverService.createModelReference(t)));
});
});
}
}
......@@ -821,12 +829,23 @@ export class ImportFileAction extends BaseFileAction {
// Run import in sequence
const importPromisesFactory: ITask<TPromise<void>>[] = [];
filesArray.forEach((file) => {
filesArray.forEach(file => {
importPromisesFactory.push(() => {
const sourceFile = URI.file(file.path);
return this.fileService.importFile(sourceFile, targetElement.resource).then(null, (error: any) => {
this.messageService.show(Severity.Error, error);
const targetFile = URI.file(paths.join(targetElement.resource.fsPath, paths.basename(file.path)));
// if the target exists and is dirty, make sure to revert it. otherwise the dirty contents
// of the target file would replace the contents of the imported file. since we already
// confirmed the overwrite before, this is OK.
let revertPromise = TPromise.as(null);
if (this.textFileService.isDirty(targetFile)) {
revertPromise = this.textFileService.revertAll([targetFile], { soft: true });
}
return revertPromise.then(() => {
return this.fileService.importFile(sourceFile, targetElement.resource).then(null, (error: any) => {
this.messageService.show(Severity.Error, error);
});
});
});
});
......
......@@ -49,6 +49,8 @@ import { Keybinding, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IMenuService, IMenu, MenuId } from 'vs/platform/actions/common/actions';
import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { ITextModelResolverService } from 'vs/editor/common/services/resolverService';
export class FileDataSource implements IDataSource {
constructor(
......@@ -723,7 +725,9 @@ export class FileDragAndDrop implements IDragAndDrop {
@IFileService private fileService: IFileService,
@IConfigurationService private configurationService: IConfigurationService,
@IInstantiationService private instantiationService: IInstantiationService,
@ITextFileService private textFileService: ITextFileService
@ITextFileService private textFileService: ITextFileService,
@ITextModelResolverService private textModelResolverService: ITextModelResolverService,
@IBackupFileService private backupFileService: IBackupFileService
) {
this.toDispose = [];
......@@ -868,70 +872,87 @@ export class FileDragAndDrop implements IDragAndDrop {
promise = tree.expand(target).then(() => {
// Reuse action if user copies
// Reuse duplicate action if user copies
if (isCopy) {
const copyAction = this.instantiationService.createInstance(DuplicateFileAction, tree, source, target);
return copyAction.run();
return this.instantiationService.createInstance(DuplicateFileAction, tree, source, target).run();
}
// Handle dirty (in file or inside the folder if any)
let revertPromise: TPromise<any> = TPromise.as(null);
const dirty = this.textFileService.getDirty().filter(d => paths.isEqualOrParent(d.fsPath, source.resource.fsPath));
if (dirty.length) {
let message: string;
if (source.isDirectory) {
if (dirty.length === 1) {
message = nls.localize('dirtyMessageFolderOne', "You are moving a folder with unsaved changes in 1 file. Do you want to continue?");
} else {
message = nls.localize('dirtyMessageFolder', "You are moving a folder with unsaved changes in {0} files. Do you want to continue?", dirty.length);
}
} else {
message = nls.localize('dirtyMessageFile', "You are moving a file with unsaved changes. Do you want to continue?");
const dirtyMoved: URI[] = [];
// Success: load all files that are dirty again to restore their dirty contents
// Error: discard any backups created during the process
const onSuccess = () => TPromise.join(dirtyMoved.map(t => this.textModelResolverService.createModelReference(t)));
const onError = (error?: Error, showError?: boolean) => {
if (showError) {
this.messageService.show(Severity.Error, error);
}
const res = this.messageService.confirm({
message,
type: 'warning',
detail: nls.localize('dirtyWarning', "Your changes will be lost if you don't save them."),
primaryButton: nls.localize({ key: 'moveLabel', comment: ['&& denotes a mnemonic'] }, "&&Move")
});
return TPromise.join(dirtyMoved.map(d => this.backupFileService.discardResourceBackup(d)));
};
if (!res) {
return TPromise.as(null);
// 1. check for dirty files that are being moved and backup to new target
const dirty = this.textFileService.getDirty().filter(d => paths.isEqualOrParent(d.fsPath, source.resource.fsPath));
return TPromise.join(dirty.map(d => {
let moved: URI;
// If the dirty file itself got moved, just reparent it to the target folder
if (source.resource.fsPath === d.fsPath) {
moved = URI.file(paths.join(target.resource.fsPath, source.name));
}
revertPromise = this.textFileService.revertAll(dirty);
}
// Otherwise, a parent of the dirty resource got moved, so we have to reparent more complicated. Example:
else {
moved = URI.file(paths.join(target.resource.fsPath, d.fsPath.substr(source.parent.resource.fsPath.length + 1)));
}
dirtyMoved.push(moved);
return revertPromise.then(() => {
const targetResource = URI.file(paths.join(target.resource.fsPath, source.name));
let didHandleConflict = false;
const model = this.textFileService.models.get(d);
// Move File/Folder
return this.fileService.moveFile(source.resource, targetResource).then(null, error => {
return this.backupFileService.backupResource(moved, model.getValue(), model.getVersionId());
}))
// Conflict
if ((<IFileOperationResult>error).fileOperationResult === FileOperationResult.FILE_MOVE_CONFLICT) {
didHandleConflict = true;
// 2. soft revert all dirty since we have backed up their contents
.then(() => this.textFileService.revertAll(dirty, { soft: true /* do not attempt to load content from disk */ }))
const confirm: IConfirmation = {
message: nls.localize('confirmOverwriteMessage', "'{0}' already exists in the destination folder. Do you want to replace it?", source.name),
detail: nls.localize('irreversible', "This action is irreversible!"),
primaryButton: nls.localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace")
};
// 3.) run the move operation
.then(() => {
const targetResource = URI.file(paths.join(target.resource.fsPath, source.name));
let didHandleConflict = false;
if (this.messageService.confirm(confirm)) {
return this.fileService.moveFile(source.resource, targetResource, true).then(null, (error) => {
this.messageService.show(Severity.Error, error);
});
return this.fileService.moveFile(source.resource, targetResource).then(null, error => {
// Conflict
if ((<IFileOperationResult>error).fileOperationResult === FileOperationResult.FILE_MOVE_CONFLICT) {
didHandleConflict = true;
const confirm: IConfirmation = {
message: nls.localize('confirmOverwriteMessage', "'{0}' already exists in the destination folder. Do you want to replace it?", source.name),
detail: nls.localize('irreversible', "This action is irreversible!"),
primaryButton: nls.localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace")
};
// Move with overwrite if the user confirms
if (this.messageService.confirm(confirm)) {
const targetDirty = this.textFileService.getDirty().filter(d => paths.isEqualOrParent(d.fsPath, targetResource.fsPath));
// Make sure to revert all dirty in target first to be able to overwrite properly
return this.textFileService.revertAll(targetDirty, { soft: true /* do not attempt to load content from disk */ }).then(() => {
// Then continue to do the move operation
return this.fileService.moveFile(source.resource, targetResource, true).then(onSuccess, error => onError(error, true));
});
}
return onError();
}
return;
}
return onError(error, true);
});
})
this.messageService.show(Severity.Error, error);
});
});
// 4.) resolve those that were dirty to load their previous dirty contents from disk
.then(onSuccess, onError);
}, errors.onUnexpectedError);
}
......
......@@ -8,9 +8,9 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import errors = require('vs/base/common/errors');
import URI from 'vs/base/common/uri';
import paths = require('vs/base/common/paths');
import { IEditor } from 'vs/editor/common/editorCommon';
import { IEditor, IEditorViewState, isCommonCodeEditor } from 'vs/editor/common/editorCommon';
import { IEditor as IBaseEditor } from 'vs/platform/editor/common/editor';
import { EditorInput, IEditorStacksModel, SideBySideEditorInput } from 'vs/workbench/common/editor';
import { toResource, EditorInput, IEditorStacksModel, SideBySideEditorInput, IEditorGroup } from 'vs/workbench/common/editor';
import { BINARY_FILE_EDITOR_ID } from 'vs/workbench/parts/files/common/files';
import { ITextFileService, ModelState } from 'vs/workbench/services/textfile/common/textfiles';
import { FileOperationEvent, FileOperation, IFileService, FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files';
......@@ -157,13 +157,42 @@ export class FileEditorTracker implements IWorkbenchContribution {
}
// Reopen
this.editorService.openEditor({ resource: reopenFileResource, options: { preserveFocus: true, pinned: group.isPinned(input), index: group.indexOf(input), inactive: !group.isActive(input) } }, stacks.positionOfGroup(group)).done(null, errors.onUnexpectedError);
this.editorService.openEditor({
resource: reopenFileResource,
options: {
preserveFocus: true,
pinned: group.isPinned(input),
index: group.indexOf(input),
inactive: !group.isActive(input),
viewState: this.getViewStateFor(oldResource, group)
}
}, stacks.positionOfGroup(group)).done(null, errors.onUnexpectedError);
}
}
});
});
}
private getViewStateFor(resource: URI, group: IEditorGroup): IEditorViewState {
const stacks = this.editorGroupService.getStacksModel();
const editors = this.editorService.getVisibleEditors();
for (let i = 0; i < editors.length; i++) {
const editor = editors[i];
if (editor && editor.position === stacks.positionOfGroup(group)) {
const resource = toResource(editor.input, { filter: 'file' });
if (resource && resource.fsPath === resource.fsPath) {
const control = editor.getControl();
if (isCommonCodeEditor(control)) {
return control.saveViewState();
}
}
}
}
return void 0;
}
private handleUpdatesToVisibleEditors(e: FileChangesEvent) {
const editors = this.editorService.getVisibleEditors();
editors.forEach(editor => {
......
......@@ -142,7 +142,6 @@ export class DefaultPreferencesEditor extends BaseTextEditor {
options.folding = false;
options.renderWhitespace = 'none';
options.wrappingColumn = 0;
options.overviewRulerLanes = 0;
options.renderIndentGuides = false;
options.rulers = [];
}
......
......@@ -125,7 +125,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
openWorkspaceSettings(): TPromise<void> {
if (!this.contextService.hasWorkspace()) {
this.messageService.show(Severity.Info, nls.localize('openFolderFirst', "Open a folder first to create workspace settings"));
return;
return TPromise.as(null);
}
return this.openSettings(ConfigurationTarget.WORKSPACE);
}
......
......@@ -91,8 +91,11 @@ export class SettingsGroupTitleWidget extends Widget implements IViewZone {
}
private layout(): void {
this.titleContainer.style.lineHeight = this.editor.getConfiguration().lineHeight + 3 + 'px';
this.titleContainer.style.fontSize = this.editor.getConfiguration().fontInfo.fontSize + 'px';
const configuration = this.editor.getConfiguration();
const layoutInfo = this.editor.getLayoutInfo();
this.titleContainer.style.width = layoutInfo.contentWidth - layoutInfo.verticalScrollbarWidth + 'px';
this.titleContainer.style.lineHeight = configuration.lineHeight + 3 + 'px';
this.titleContainer.style.fontSize = configuration.fontInfo.fontSize + 'px';
const iconSize = this.getIconSize();
this.icon.style.height = `${iconSize}px`;
this.icon.style.width = `${iconSize}px`;
......
......@@ -470,6 +470,7 @@ export class SearchViewlet extends Viewlet {
}
// Reveal the newly selected element
this.tree.setFocus(next, eventPayload);
this.tree.setSelection([next], eventPayload);
this.tree.reveal(next);
}
......@@ -497,7 +498,9 @@ export class SearchViewlet extends Viewlet {
}
// Reveal the newly selected element
this.tree.setFocus(prev, eventPayload);
this.tree.setSelection([prev], eventPayload);
this.tree.reveal(prev);
}
public setVisible(visible: boolean): TPromise<void> {
......
......@@ -229,11 +229,6 @@ class PatternBuilder {
this.result.loop = value;
return this;
}
public mostSignifikant(value: boolean): PatternBuilder {
this.result.mostSignifikant = value;
return this;
}
}
......@@ -1007,6 +1002,5 @@ suite('Tasks Configuration parsing tests', () => {
assert.strictEqual(actual.code, expected.code);
assert.strictEqual(actual.severity, expected.severity);
assert.strictEqual(actual.loop, expected.loop);
assert.strictEqual(actual.mostSignifikant, expected.mostSignifikant);
}
});
\ No newline at end of file
......@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IConfiguration, DefaultConfig } from 'vs/editor/common/config/defaultConfig';
import { IConfiguration as IEditorConfiguration, DefaultConfig } from 'vs/editor/common/config/defaultConfig';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ITerminalConfiguration, ITerminalConfigHelper, ITerminalFont, IShell } from 'vs/workbench/parts/terminal/common/terminal';
import { Platform } from 'vs/base/common/platform';
......@@ -91,16 +91,16 @@ export class TerminalConfigHelper implements ITerminalConfigHelper {
this._charMeasureElement = document.createElement('div');
this.panelContainer.appendChild(this._charMeasureElement);
}
let style = this._charMeasureElement.style;
const style = this._charMeasureElement.style;
style.display = 'block';
style.fontFamily = fontFamily;
style.fontSize = fontSize + 'px';
style.height = Math.floor(lineHeight * fontSize) + 'px';
style.lineHeight = lineHeight.toString(10);
this._charMeasureElement.innerText = 'X';
let rect = this._charMeasureElement.getBoundingClientRect();
const rect = this._charMeasureElement.getBoundingClientRect();
style.display = 'none';
let charWidth = Math.ceil(rect.width);
let charHeight = Math.ceil(rect.height);
const charWidth = rect.width;
const charHeight = rect.height;
return {
fontFamily,
fontSize: fontSize + 'px',
......@@ -115,37 +115,46 @@ export class TerminalConfigHelper implements ITerminalConfigHelper {
* terminal.integrated.fontSize, terminal.integrated.lineHeight configuration properties
*/
public getFont(): ITerminalFont {
let terminalConfig = this._configurationService.getConfiguration<ITerminalConfiguration>().terminal.integrated;
let editorConfig = this._configurationService.getConfiguration<IConfiguration>();
const config = this._configurationService.getConfiguration();
const editorConfig = (<IEditorConfiguration>config).editor;
const terminalConfig = (<ITerminalConfiguration>config).terminal.integrated;
let fontFamily = terminalConfig.fontFamily || editorConfig.editor.fontFamily;
const fontFamily = terminalConfig.fontFamily || editorConfig.fontFamily;
let fontSize = this._toInteger(terminalConfig.fontSize, 0);
if (fontSize <= 0) {
fontSize = DefaultConfig.editor.fontSize;
}
let lineHeight = terminalConfig.lineHeight <= 0 ? DEFAULT_LINE_HEIGHT : terminalConfig.lineHeight;
if (!lineHeight) {
lineHeight = DEFAULT_LINE_HEIGHT;
}
return this._measureFont(fontFamily, fontSize, lineHeight);
}
public getFontLigaturesEnabled(): boolean {
let terminalConfig = this._configurationService.getConfiguration<ITerminalConfiguration>().terminal.integrated;
return terminalConfig.fontLigatures;
const terminalConfig = this._configurationService.getConfiguration<ITerminalConfiguration>();
return terminalConfig.terminal.integrated.fontLigatures;
}
public getCursorBlink(): boolean {
let terminalConfig = this._configurationService.getConfiguration<ITerminalConfiguration>().terminal.integrated;
return terminalConfig.cursorBlinking;
const terminalConfig = this._configurationService.getConfiguration<ITerminalConfiguration>();
return terminalConfig.terminal.integrated.cursorBlinking;
}
public getRightClickCopyPaste(): boolean {
let config = this._configurationService.getConfiguration<ITerminalConfiguration>();
const config = this._configurationService.getConfiguration<ITerminalConfiguration>();
return config.terminal.integrated.rightClickCopyPaste;
}
public getCommandsToSkipShell(): string[] {
const config = this._configurationService.getConfiguration<ITerminalConfiguration>();
return config.terminal.integrated.commandsToSkipShell;
}
public getShell(): IShell {
let config = this._configurationService.getConfiguration<ITerminalConfiguration>();
let shell: IShell = {
const config = this._configurationService.getConfiguration<ITerminalConfiguration>();
const shell: IShell = {
executable: '',
args: []
};
......@@ -190,9 +199,4 @@ export class TerminalConfigHelper implements ITerminalConfigHelper {
}
return r;
}
public getCommandsToSkipShell(): string[] {
let config = this._configurationService.getConfiguration<ITerminalConfiguration>();
return config.terminal.integrated.commandsToSkipShell;
}
}
\ No newline at end of file
......@@ -156,8 +156,8 @@ export class TerminalInstance implements ITerminalInstance {
}, 0);
});
let xtermHelper: HTMLElement = this._xterm.element.querySelector('.xterm-helpers');
let focusTrap: HTMLElement = document.createElement('div');
const xtermHelper: HTMLElement = this._xterm.element.querySelector('.xterm-helpers');
const focusTrap: HTMLElement = document.createElement('div');
focusTrap.setAttribute('tabindex', '0');
DOM.addClass(focusTrap, 'focus-trap');
focusTrap.addEventListener('focus', function (event: FocusEvent) {
......@@ -165,7 +165,7 @@ export class TerminalInstance implements ITerminalInstance {
while (!DOM.hasClass(currentElement, 'part')) {
currentElement = currentElement.parentElement;
}
let hidePanelElement = <HTMLElement>currentElement.querySelector('.hide-panel-action');
const hidePanelElement = <HTMLElement>currentElement.querySelector('.hide-panel-action');
hidePanelElement.focus();
});
xtermHelper.insertBefore(focusTrap, this._xterm.textarea);
......@@ -240,7 +240,7 @@ export class TerminalInstance implements ITerminalInstance {
if (!this._xterm) {
return;
}
let text = window.getSelection().toString();
const text = window.getSelection().toString();
if (!text || force) {
this._xterm.focus();
}
......@@ -331,11 +331,11 @@ export class TerminalInstance implements ITerminalInstance {
}
protected _createProcess(workspace: IWorkspace, name: string, shell: IShell) {
let locale = this._configHelper.isSetLocaleVariables() ? platform.locale : undefined;
const locale = this._configHelper.isSetLocaleVariables() ? platform.locale : undefined;
if (!shell.executable) {
shell = this._configHelper.getShell();
}
let env = TerminalInstance.createTerminalEnv(process.env, shell, this._getCwd(workspace, shell.ignoreCustomCwd), locale);
const env = TerminalInstance.createTerminalEnv(process.env, shell, this._getCwd(workspace, shell.ignoreCustomCwd), locale);
this._title = name ? name : '';
this._process = cp.fork('./terminalProcess', [], {
env: env,
......@@ -378,7 +378,7 @@ export class TerminalInstance implements ITerminalInstance {
// TODO: This should be private/protected
// TODO: locale should not be optional
public static createTerminalEnv(parentEnv: IStringDictionary<string>, shell: IShell, cwd: string, locale?: string): IStringDictionary<string> {
let env = TerminalInstance._cloneEnv(parentEnv);
const env = TerminalInstance._cloneEnv(parentEnv);
env['PTYPID'] = process.pid.toString();
env['PTYSHELL'] = shell.executable;
if (shell.args) {
......@@ -402,7 +402,7 @@ export class TerminalInstance implements ITerminalInstance {
}
private static _cloneEnv(env: IStringDictionary<string>): IStringDictionary<string> {
let newEnv: IStringDictionary<string> = Object.create(null);
const newEnv: IStringDictionary<string> = Object.create(null);
Object.keys(env).forEach((key) => {
newEnv[key] = env[key];
});
......@@ -442,7 +442,7 @@ export class TerminalInstance implements ITerminalInstance {
}
public layout(dimension: { width: number, height: number }): void {
let font = this._configHelper.getFont();
const font = this._configHelper.getFont();
if (!font || !font.charWidth || !font.charHeight) {
return;
}
......@@ -455,10 +455,12 @@ export class TerminalInstance implements ITerminalInstance {
// Upstream issue: https://github.com/sourcelair/xterm.js/issues/291
this._xterm.emit('scroll', this._xterm.ydisp);
}
let leftPadding = parseInt(getComputedStyle(document.querySelector('.terminal-outer-container')).paddingLeft.split('px')[0], 10);
let innerWidth = dimension.width - leftPadding;
let cols = Math.floor(innerWidth / font.charWidth);
let rows = Math.floor(dimension.height / font.charHeight);
const padding = parseInt(getComputedStyle(document.querySelector('.terminal-outer-container')).paddingLeft.split('px')[0], 10);
// Use left padding as right padding, right padding is not defined in CSS just in case
// xterm.js causes an unexpected overflow.
const innerWidth = dimension.width - padding * 2;
const cols = Math.floor(innerWidth / font.charWidth);
const rows = Math.floor(dimension.height / font.charHeight);
if (this._xterm) {
this._xterm.resize(cols, rows);
this._xterm.element.style.width = innerWidth + 'px';
......
......@@ -6,7 +6,7 @@
var fs = require('fs');
var os = require('os');
var path = require('path');
var ptyJs = require('pty.js');
var ptyJs = require('node-pty');
// The pty process needs to be run in its own child process to get around maxing out CPU on Mac,
// see https://github.com/electron/electron/issues/38
......
......@@ -207,7 +207,7 @@ suite('ExtHostDocument', () => {
test('getWordRangeAtPosition', function () {
data = new ExtHostDocumentData(undefined, URI.file(''), [
'aaaa bbbb cccc abc'
'aaaa bbbb+cccc abc'
], '\n', 'text', 1, false);
let range = data.getWordRangeAtPosition(new Position(0, 2));
......@@ -216,19 +216,20 @@ suite('ExtHostDocument', () => {
assert.equal(range.end.line, 0);
assert.equal(range.end.character, 4);
// ignore bad regular expresson /.*/
range = data.getWordRangeAtPosition(new Position(0, 2), /.*/);
assert.equal(range.start.line, 0);
assert.equal(range.start.character, 0);
assert.equal(range.end.line, 0);
assert.equal(range.end.character, 4);
range = data.getWordRangeAtPosition(new Position(0, 2), /a+.+?c/);
range = data.getWordRangeAtPosition(new Position(0, 5), /[a-z+]+/);
assert.equal(range.start.line, 0);
assert.equal(range.start.character, 0);
assert.equal(range.start.character, 5);
assert.equal(range.end.line, 0);
assert.equal(range.end.character, 11);
assert.equal(range.end.character, 14);
range = data.getWordRangeAtPosition(new Position(0, 17), /a+.+?c/);
range = data.getWordRangeAtPosition(new Position(0, 17), /[a-z+]+/);
assert.equal(range.start.line, 0);
assert.equal(range.start.character, 15);
assert.equal(range.end.line, 0);
......
......@@ -774,10 +774,6 @@ export class TestWindowService implements IWindowService {
return TPromise.as(void 0);
}
toggleMenuBar(): TPromise<void> {
return TPromise.as(void 0);
}
isMaximized(): TPromise<boolean> {
return TPromise.as(void 0);
}
......@@ -879,9 +875,6 @@ export class TestWindowsService implements IWindowsService {
setDocumentEdited(windowId: number, flag: boolean): TPromise<void> {
return TPromise.as(void 0);
}
toggleMenuBar(windowId: number): TPromise<void> {
return TPromise.as(void 0);
}
quit(): TPromise<void> {
return TPromise.as(void 0);
}
......@@ -923,4 +916,4 @@ export class TestWindowsService implements IWindowsService {
startCrashReporter(config: Electron.CrashReporterStartOptions): TPromise<void> {
return TPromise.as(void 0);
}
}
\ No newline at end of file
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册