提交 de389277 编写于 作者: D Dirk Baeumer

Merge branch 'master' into dbaeumer/27078

......@@ -2,17 +2,27 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// @ts-check
'use strict';
(function () {
const unloadedStyles = [];
window.onStyleLoadError = (event) => {
const onStyleLoadError = (event) => {
const source = event.target.dataset.source;
unloadedStyles.push(source);
};
window.addEventListener('DOMContentLoaded', () => {
for (const link of document.getElementsByClassName('code-user-style')) {
if (link.dataset.source) {
link.onerror = onStyleLoadError;
}
}
})
window.addEventListener('load', () => {
if (!unloadedStyles.length) {
return;
......
......@@ -166,7 +166,7 @@ export class MDDocumentContentProvider implements vscode.TextDocumentContentProv
private computeCustomStyleSheetIncludes(uri: vscode.Uri): string {
if (this.config.styles && Array.isArray(this.config.styles)) {
return this.config.styles.map((style) => {
return `<link rel="stylesheet" data-source="${style.replace(/"/g, '&quot;')}" onerror="onStyleLoadError(event)" href="${this.fixHref(uri, style)}" type="text/css" media="screen">`;
return `<link rel="stylesheet" class="code-user-style" data-source="${style.replace(/"/g, '&quot;')}" href="${this.fixHref(uri, style)}" type="text/css" media="screen">`;
}).join('\n');
}
return '';
......
......@@ -6,4 +6,4 @@
Steps to Reproduce:
1.
2.
1.
......@@ -8,31 +8,33 @@ import * as Platform from 'vs/base/common/platform';
import Event, { Emitter } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
export const enum AccessibilitySupport {
/**
* This should be the browser case where it is not known if a screen reader is attached or no.
*/
Unknown = 0,
Disabled = 1,
Enabled = 2
}
class WindowManager {
public static INSTANCE = new WindowManager();
private _fullscreen: boolean;
// --- Zoom Level
private _zoomLevel: number = 0;
private _lastZoomLevelChangeTime: number = 0;
private _zoomFactor: number = 0;
private _onDidChangeZoomLevel: Emitter<number> = new Emitter<number>();
public onDidChangeZoomLevel: Event<number> = this._onDidChangeZoomLevel.event;
private _onDidChangeFullscreen: Emitter<void> = new Emitter<void>();
public onDidChangeFullscreen: Event<void> = this._onDidChangeFullscreen.event;
public onDidChangeZoomLevel: Event<number> = this._onDidChangeZoomLevel.event;
public getZoomLevel(): number {
return this._zoomLevel;
}
public getTimeSinceLastZoomLevelChanged(): number {
return Date.now() - this._lastZoomLevelChangeTime;
}
public setZoomLevel(zoomLevel: number, isTrusted: boolean): void {
if (this._zoomLevel === zoomLevel) {
return;
......@@ -44,14 +46,19 @@ class WindowManager {
this._onDidChangeZoomLevel.fire(this._zoomLevel);
}
// --- Zoom Factor
private _zoomFactor: number = 0;
public getZoomFactor(): number {
return this._zoomFactor;
}
public setZoomFactor(zoomFactor: number): void {
this._zoomFactor = zoomFactor;
}
// --- Pixel Ratio
public getPixelRatio(): number {
let ctx = document.createElement('canvas').getContext('2d');
let dpr = window.devicePixelRatio || 1;
......@@ -63,6 +70,11 @@ class WindowManager {
return dpr / bsr;
}
// --- Fullscreen
private _fullscreen: boolean;
private _onDidChangeFullscreen: Emitter<void> = new Emitter<void>();
public onDidChangeFullscreen: Event<void> = this._onDidChangeFullscreen.event;
public setFullscreen(fullscreen: boolean): void {
if (this._fullscreen === fullscreen) {
return;
......@@ -71,10 +83,28 @@ class WindowManager {
this._fullscreen = fullscreen;
this._onDidChangeFullscreen.fire();
}
public isFullscreen(): boolean {
return this._fullscreen;
}
// --- Accessibility
private _accessibilitySupport = AccessibilitySupport.Unknown;
private _onDidChangeAccessibilitySupport: Emitter<void> = new Emitter<void>();
public onDidChangeAccessibilitySupport: Event<void> = this._onDidChangeAccessibilitySupport.event;
public setAccessibilitySupport(accessibilitySupport: AccessibilitySupport): void {
if (this._accessibilitySupport === accessibilitySupport) {
return;
}
this._accessibilitySupport = accessibilitySupport;
this._onDidChangeAccessibilitySupport.fire();
}
public getAccessibilitySupport(): AccessibilitySupport {
return this._accessibilitySupport;
}
}
/** A zoom index, e.g. 1, 2, 3 */
......@@ -88,19 +118,22 @@ export function getZoomLevel(): number {
export function getTimeSinceLastZoomLevelChanged(): number {
return WindowManager.INSTANCE.getTimeSinceLastZoomLevelChanged();
}
export function onDidChangeZoomLevel(callback: (zoomLevel: number) => void): IDisposable {
return WindowManager.INSTANCE.onDidChangeZoomLevel(callback);
}
/** The zoom scale for an index, e.g. 1, 1.2, 1.4 */
export function getZoomFactor(): number {
return WindowManager.INSTANCE.getZoomFactor();
}
export function getPixelRatio(): number {
return WindowManager.INSTANCE.getPixelRatio();
}
export function setZoomFactor(zoomFactor: number): void {
WindowManager.INSTANCE.setZoomFactor(zoomFactor);
}
export function onDidChangeZoomLevel(callback: (zoomLevel: number) => void): IDisposable {
return WindowManager.INSTANCE.onDidChangeZoomLevel(callback);
export function getPixelRatio(): number {
return WindowManager.INSTANCE.getPixelRatio();
}
export function setFullscreen(fullscreen: boolean): void {
WindowManager.INSTANCE.setFullscreen(fullscreen);
}
......@@ -111,6 +144,16 @@ export function onDidChangeFullscreen(callback: () => void): IDisposable {
return WindowManager.INSTANCE.onDidChangeFullscreen(callback);
}
export function setAccessibilitySupport(accessibilitySupport: AccessibilitySupport): void {
WindowManager.INSTANCE.setAccessibilitySupport(accessibilitySupport);
}
export function getAccessibilitySupport(): AccessibilitySupport {
return WindowManager.INSTANCE.getAccessibilitySupport();
}
export function onDidChangeAccessibilitySupport(callback: () => void): IDisposable {
return WindowManager.INSTANCE.onDidChangeAccessibilitySupport(callback);
}
const userAgent = navigator.userAgent;
export const isIE = (userAgent.indexOf('Trident') >= 0);
......
......@@ -79,9 +79,13 @@ export interface IWindowConfiguration extends ParsedArgs {
userEnv: platform.IProcessEnvironment;
/**
* The physical keyboard is of ISO type (on OSX)
* The physical keyboard is of ISO type (on OSX).
*/
isISOKeyboard?: boolean;
/**
* Accessibility support is enabled.
*/
accessibilitySupportEnabled?: boolean;
zoomLevel?: number;
fullscreen?: boolean;
highContrast?: boolean;
......
......@@ -177,6 +177,11 @@ export class WindowsManager implements IWindowsMainService {
}
private registerListeners(): void {
app.on('accessibility-support-changed', (event: Event, accessibilitySupportEnabled: boolean) => {
this.sendToAll('vscode:accessibilitySupportChanged', accessibilitySupportEnabled);
});
app.on('activate', (event: Event, hasVisibleWindows: boolean) => {
this.logService.log('App#activate');
......@@ -733,6 +738,7 @@ export class WindowsManager implements IWindowsMainService {
configuration.filesToDiff = filesToDiff;
configuration.nodeCachedDataDir = this.environmentService.nodeCachedDataDir;
configuration.isISOKeyboard = KeyboardLayoutMonitor.INSTANCE.isISOKeyboard();
configuration.accessibilitySupportEnabled = app.isAccessibilitySupportEnabled();
return configuration;
}
......
......@@ -765,6 +765,7 @@ export interface IValidatedEditorOptions {
readonly dragAndDrop: boolean;
readonly emptySelectionClipboard: boolean;
readonly useTabStops: boolean;
readonly performanceCritical: boolean;
readonly viewInfo: InternalEditorViewOptions;
readonly contribInfo: EditorContribOptions;
......@@ -1344,8 +1345,9 @@ export class EditorOptionsValidator {
wordWrap = _stringSet<'off' | 'on' | 'wordWrapColumn' | 'bounded'>(wordWrap, defaults.wordWrap, ['off', 'on', 'wordWrapColumn', 'bounded']);
}
const performanceCritical = false;
const viewInfo = this._sanitizeViewInfo(opts, defaults.viewInfo);
const contribInfo = this._sanitizeContribInfo(opts, defaults.contribInfo);
const contribInfo = this._sanitizeContribInfo(opts, defaults.contribInfo, performanceCritical);
return {
inDiffEditor: _boolean(opts.inDiffEditor, defaults.inDiffEditor),
......@@ -1367,6 +1369,7 @@ export class EditorOptionsValidator {
dragAndDrop: _boolean(opts.dragAndDrop, defaults.dragAndDrop),
emptySelectionClipboard: _boolean(opts.emptySelectionClipboard, defaults.emptySelectionClipboard),
useTabStops: _boolean(opts.useTabStops, defaults.useTabStops),
performanceCritical: performanceCritical,
viewInfo: viewInfo,
contribInfo: contribInfo,
};
......@@ -1514,7 +1517,7 @@ export class EditorOptionsValidator {
};
}
private static _sanitizeContribInfo(opts: IEditorOptions, defaults: EditorContribOptions): EditorContribOptions {
private static _sanitizeContribInfo(opts: IEditorOptions, defaults: EditorContribOptions, performanceCritical: boolean): EditorContribOptions {
let quickSuggestions: boolean | { other: boolean, comments: boolean, strings: boolean };
if (typeof opts.quickSuggestions === 'object') {
quickSuggestions = { other: true, ...opts.quickSuggestions };
......@@ -1538,12 +1541,12 @@ export class EditorOptionsValidator {
wordBasedSuggestions: _boolean(opts.wordBasedSuggestions, defaults.wordBasedSuggestions),
suggestFontSize: _clampedInt(opts.suggestFontSize, defaults.suggestFontSize, 0, 1000),
suggestLineHeight: _clampedInt(opts.suggestLineHeight, defaults.suggestLineHeight, 0, 1000),
selectionHighlight: _boolean(opts.selectionHighlight, defaults.selectionHighlight),
occurrencesHighlight: _boolean(opts.occurrencesHighlight, defaults.occurrencesHighlight),
codeLens: _boolean(opts.codeLens, defaults.codeLens) && _boolean(opts.referenceInfos, true),
folding: _boolean(opts.folding, defaults.folding),
selectionHighlight: !performanceCritical && _boolean(opts.selectionHighlight, defaults.selectionHighlight),
occurrencesHighlight: !performanceCritical && _boolean(opts.occurrencesHighlight, defaults.occurrencesHighlight),
codeLens: !performanceCritical && _boolean(opts.codeLens, defaults.codeLens) && _boolean(opts.referenceInfos, true),
folding: !performanceCritical && _boolean(opts.folding, defaults.folding),
showFoldingControls: _stringSet<'always' | 'mouseover'>(opts.showFoldingControls, defaults.showFoldingControls, ['always', 'mouseover']),
matchBrackets: _boolean(opts.matchBrackets, defaults.matchBrackets),
matchBrackets: !performanceCritical && _boolean(opts.matchBrackets, defaults.matchBrackets),
};
}
}
......@@ -1879,6 +1882,7 @@ export const EDITOR_DEFAULTS: IValidatedEditorOptions = {
dragAndDrop: false,
emptySelectionClipboard: true,
useTabStops: true,
performanceCritical: false,
viewInfo: {
extraEditorClassName: '',
......
......@@ -21,7 +21,7 @@ import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents';
import { IViewModel } from "vs/editor/common/viewModel/viewModel";
import * as viewEvents from 'vs/editor/common/view/viewEvents';
import Event, { Emitter } from 'vs/base/common/event';
import { ScreenReaderMessageGenerator } from "vs/editor/common/controller/accGenerator";
// import { ScreenReaderMessageGenerator } from "vs/editor/common/controller/accGenerator";
function containsLineMappingChanged(events: viewEvents.ViewEvent[]): boolean {
for (let i = 0, len = events.length; i < len; i++) {
......@@ -382,16 +382,16 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
const viewSelections = this._cursors.getViewSelections();
let screenReaderMessage: string = null;
if (oldState) {
screenReaderMessage = ScreenReaderMessageGenerator.generateMessage(
source,
this._model,
oldState.modelVersionId,
oldState.cursorState[0].modelState.selection,
newState.modelVersionId,
newState.cursorState[0].modelState.selection
);
}
// if (oldState) {
// screenReaderMessage = ScreenReaderMessageGenerator.generateMessage(
// source,
// this._model,
// oldState.modelVersionId,
// oldState.cursorState[0].modelState.selection,
// newState.modelVersionId,
// newState.cursorState[0].modelState.selection
// );
// }
// Let the view get the event first.
this._emit([new viewEvents.ViewCursorStateChangedEvent(viewSelections, isInEditableRange, screenReaderMessage)]);
......
......@@ -880,7 +880,7 @@ let lastStaticId = 0;
export class ModelDecorationOptions implements editorCommon.IModelDecorationOptions {
public static EMPTY = ModelDecorationOptions.register({});
public static EMPTY: ModelDecorationOptions;
public static register(options: editorCommon.IModelDecorationOptions): ModelDecorationOptions {
return new ModelDecorationOptions(++lastStaticId, options);
......@@ -944,6 +944,7 @@ export class ModelDecorationOptions implements editorCommon.IModelDecorationOpti
);
}
}
ModelDecorationOptions.EMPTY = ModelDecorationOptions.register({});
class ModelDeltaDecoration implements editorCommon.IModelDeltaDecoration {
......
......@@ -82,7 +82,16 @@ export class BracketMatchingController extends Disposable implements editorCommo
this._matchBrackets = this._editor.getConfiguration().contribInfo.matchBrackets;
this._updateBracketsSoon.schedule();
this._register(editor.onDidChangeCursorPosition((e) => this._updateBracketsSoon.schedule()));
this._register(editor.onDidChangeCursorPosition((e) => {
if (!this._matchBrackets) {
// Early exit if nothing needs to be done!
// Leave some form of early exit check here if you wish to continue being a cursor position change listener ;)
return;
}
this._updateBracketsSoon.schedule();
}));
this._register(editor.onDidChangeModel((e) => { this._decorations = []; this._updateBracketsSoon.schedule(); }));
this._register(editor.onDidChangeConfiguration((e) => {
this._matchBrackets = this._editor.getConfiguration().contribInfo.matchBrackets;
......
......@@ -928,6 +928,7 @@ export class SelectionHighlighter extends Disposable implements editorCommon.IEd
private static ID = 'editor.contrib.selectionHighlighter';
private editor: editorCommon.ICommonCodeEditor;
private _isEnabled: boolean;
private decorations: string[];
private updateSoon: RunOnceScheduler;
private state: SelectionHighlighterState;
......@@ -935,11 +936,22 @@ export class SelectionHighlighter extends Disposable implements editorCommon.IEd
constructor(editor: editorCommon.ICommonCodeEditor) {
super();
this.editor = editor;
this._isEnabled = editor.getConfiguration().contribInfo.selectionHighlight;
this.decorations = [];
this.updateSoon = this._register(new RunOnceScheduler(() => this._update(), 300));
this.state = null;
this._register(editor.onDidChangeConfiguration((e) => {
this._isEnabled = editor.getConfiguration().contribInfo.selectionHighlight;
}));
this._register(editor.onDidChangeCursorSelection((e: ICursorSelectionChangedEvent) => {
if (!this._isEnabled) {
// Early exit if nothing needs to be done!
// Leave some form of early exit check here if you wish to continue being a cursor position change listener ;)
return;
}
if (e.selection.isEmpty()) {
if (e.reason === CursorChangeReason.Explicit) {
if (this.state && (!this.state.lastWordUnderCursor || !this.state.lastWordUnderCursor.containsPosition(e.selection.getStartPosition()))) {
......@@ -968,10 +980,10 @@ export class SelectionHighlighter extends Disposable implements editorCommon.IEd
}
private _update(): void {
this._setState(SelectionHighlighter._createState(this.editor));
this._setState(SelectionHighlighter._createState(this._isEnabled, this.editor));
}
private static _createState(editor: editorCommon.ICommonCodeEditor): SelectionHighlighterState {
private static _createState(isEnabled: boolean, editor: editorCommon.ICommonCodeEditor): SelectionHighlighterState {
const model = editor.getModel();
if (!model) {
return null;
......@@ -980,7 +992,7 @@ export class SelectionHighlighter extends Disposable implements editorCommon.IEd
const config = editor.getConfiguration();
let lastWordUnderCursor: Selection = null;
if (!config.contribInfo.selectionHighlight) {
if (!isEnabled) {
return null;
}
......
......@@ -219,7 +219,16 @@ export class FoldingController implements IFoldingController {
this.localToDispose.push(this.cursorChangedScheduler);
this.localToDispose.push(this.editor.onDidChangeModelContent(e => this.contentChangedScheduler.schedule()));
this.localToDispose.push(this.editor.onDidChangeCursorPosition(e => this.cursorChangedScheduler.schedule()));
this.localToDispose.push(this.editor.onDidChangeCursorPosition((e) => {
if (!this._isEnabled) {
// Early exit if nothing needs to be done!
// Leave some form of early exit check here if you wish to continue being a cursor position change listener ;)
return;
}
this.cursorChangedScheduler.schedule();
}));
this.localToDispose.push(this.editor.onMouseDown(e => this.onEditorMouseDown(e)));
this.localToDispose.push(this.editor.onMouseUp(e => this.onEditorMouseUp(e)));
......
......@@ -13,6 +13,18 @@ import { ISnippetVariableResolver } from './snippet';
export class EditorSnippetVariableResolver {
static readonly VariableNames = Object.freeze({
'SELECTION': true,
'TM_SELECTED_TEXT': true,
'TM_CURRENT_LINE': true,
'TM_CURRENT_WORD': true,
'TM_LINE_INDEX': true,
'TM_LINE_NUMBER': true,
'TM_FILENAME': true,
'TM_DIRECTORY': true,
'TM_FILEPATH': true,
});
constructor(
private readonly _model: IModel,
private readonly _selection: Selection
......
......@@ -199,7 +199,7 @@ export class SuggestModel implements IDisposable {
}
}
}
this.trigger(true, false, supports, items);
this.trigger(true, Boolean(this.completionModel), supports, items);
}
});
}
......@@ -248,6 +248,12 @@ export class SuggestModel implements IDisposable {
|| e.source !== 'keyboard'
|| e.reason !== CursorChangeReason.NotSet) {
if (this._state === State.Idle) {
// Early exit if nothing needs to be done!
// Leave some form of early exit check here if you wish to continue being a cursor position change listener ;)
return;
}
this.cancel();
return;
}
......
......@@ -78,6 +78,13 @@ class WordHighlighter {
this.model = this.editor.getModel();
this.toUnhook = [];
this.toUnhook.push(editor.onDidChangeCursorPosition((e: ICursorPositionChangedEvent) => {
if (!this.occurrencesHighlight) {
// Early exit if nothing needs to be done!
// Leave some form of early exit check here if you wish to continue being a cursor position change listener ;)
return;
}
this._onPositionChanged(e);
}));
this.toUnhook.push(editor.onDidChangeModel((e) => {
......
......@@ -3357,7 +3357,9 @@ suite('cursor screen reader message', () => {
callback(editor, cursor);
assert.equal(actualScreenReaderMessage, expectedScreenReaderMessage);
// Disabled for now
assert.equal(actualScreenReaderMessage, null);
// assert.equal(actualScreenReaderMessage, expectedScreenReaderMessage);
});
}
......
......@@ -5,7 +5,8 @@
'use strict';
import { ILocalExtension, IGalleryExtension, IExtensionManifest, EXTENSION_IDENTIFIER_REGEX } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ILocalExtension, IGalleryExtension, IExtensionManifest, EXTENSION_IDENTIFIER_REGEX, IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
export function areSameExtensions(a: { id: string }, b: { id: string }): boolean {
if (a.id === b.id) {
......@@ -71,4 +72,26 @@ export function getGalleryExtensionTelemetryData(extension: IGalleryExtension):
publisherDisplayName: extension.publisherDisplayName,
dependencies: extension.properties.dependencies.length > 0
};
}
const BetterMergeCheckKey = 'extensions/bettermergecheck';
export const BetterMergeDisabledNowKey = 'extensions/bettermergedisablednow';
export const BetterMergeId = 'pprice.better-merge';
/**
* Globally disabled extensions, taking care of disabling obsolete extensions.
*/
const CHECK = false;
export function getGloballyDisabledExtensions(extensionEnablementService: IExtensionEnablementService, storageService: IStorageService, installedExtensions: { id: string; }[]) {
const globallyDisabled = extensionEnablementService.getGloballyDisabledExtensions();
if (CHECK && !storageService.getBoolean(BetterMergeCheckKey, StorageScope.GLOBAL, false)) {
storageService.store(BetterMergeCheckKey, true);
if (globallyDisabled.indexOf(BetterMergeId) === -1 && installedExtensions.some(d => d.id === BetterMergeId)) {
globallyDisabled.push(BetterMergeId);
extensionEnablementService.setEnablement(BetterMergeId, false);
storageService.store(BetterMergeDisabledNowKey, true);
}
}
return globallyDisabled;
}
\ No newline at end of file
......@@ -32,6 +32,8 @@ export interface IMessage {
type: Severity;
message: string;
source: string;
extensionId: string;
extensionPointId: string;
}
export interface IExtensionsStatus {
......
......@@ -17,19 +17,27 @@ const schemaRegistry = <IJSONContributionRegistry>Registry.as(Extensions.JSONCon
export class ExtensionMessageCollector {
private _messageHandler: (msg: IMessage) => void;
private _source: string;
constructor(messageHandler: (msg: IMessage) => void, source: string) {
private readonly _messageHandler: (msg: IMessage) => void;
private readonly _extension: IExtensionDescription;
private readonly _extensionPointId: string;
constructor(
messageHandler: (msg: IMessage) => void,
extension: IExtensionDescription,
extensionPointId: string
) {
this._messageHandler = messageHandler;
this._source = source;
this._extension = extension;
this._extensionPointId = extensionPointId;
}
private _msg(type: Severity, message: string): void {
this._messageHandler({
type: type,
message: message,
source: this._source
source: this._extension.extensionFolderPath,
extensionId: this._extension.id,
extensionPointId: this._extensionPointId
});
}
......
......@@ -13,7 +13,7 @@ import URI from 'vs/base/common/uri';
import { AbstractExtensionService, ActivatedExtension } from 'vs/platform/extensions/common/abstractExtensionService';
import { IMessage, IExtensionDescription, IExtensionsStatus } from 'vs/platform/extensions/common/extensions';
import { IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { areSameExtensions, getGloballyDisabledExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionsRegistry, ExtensionPoint, IExtensionPointUser, ExtensionMessageCollector } from 'vs/platform/extensions/common/extensionsRegistry';
import { ExtensionScanner, MessagesCollector } from 'vs/workbench/node/extensionPoints';
import { IMessageService } from 'vs/platform/message/common/message';
......@@ -21,6 +21,7 @@ import { IThreadService } from 'vs/workbench/services/thread/common/threadServic
import { ExtHostContext, ExtHostExtensionServiceShape } from '../node/extHost.protocol';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IStorageService } from 'vs/platform/storage/common/storage';
const SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions'));
......@@ -51,8 +52,6 @@ const hasOwnProperty = Object.hasOwnProperty;
export class MainProcessExtensionService extends AbstractExtensionService<ActivatedExtension> {
private _threadService: IThreadService;
private _messageService: IMessageService;
private _proxy: ExtHostExtensionServiceShape;
private _isDev: boolean;
private _extensionsStatus: { [id: string]: IExtensionsStatus };
......@@ -61,28 +60,26 @@ export class MainProcessExtensionService extends AbstractExtensionService<Activa
* This class is constructed manually because it is a service, so it doesn't use any ctor injection
*/
constructor(
@IThreadService threadService: IThreadService,
@IMessageService messageService: IMessageService,
@IThreadService private readonly _threadService: IThreadService,
@IMessageService private readonly _messageService: IMessageService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@IExtensionEnablementService extensionEnablementService: IExtensionEnablementService,
@ITelemetryService telemetryService: ITelemetryService,
@IEnvironmentService private environmentService: IEnvironmentService,
@IStorageService storageService: IStorageService,
) {
super(false);
this._isDev = !environmentService.isBuilt || environmentService.isExtensionDevelopment;
this._messageService = messageService;
this._threadService = threadService;
this._proxy = this._threadService.get(ExtHostContext.ExtHostExtensionService);
this._extensionsStatus = {};
const disabledExtensions = [
...extensionEnablementService.getGloballyDisabledExtensions(),
...extensionEnablementService.getWorkspaceDisabledExtensions()
];
this.scanExtensions().done(extensionDescriptions => {
const disabledExtensions = [
...getGloballyDisabledExtensions(extensionEnablementService, storageService, extensionDescriptions),
...extensionEnablementService.getWorkspaceDisabledExtensions()
];
telemetryService.publicLog('extensionsScanned', {
_telemetryService.publicLog('extensionsScanned', {
totalCount: extensionDescriptions.length,
disabledCount: disabledExtensions.length
});
......@@ -98,6 +95,13 @@ export class MainProcessExtensionService extends AbstractExtensionService<Activa
this._extensionsStatus[msg.source] = { messages: [] };
}
this._extensionsStatus[msg.source].messages.push(msg);
if (!this._isDev && msg.extensionId) {
const { type, extensionId, extensionPointId, message } = msg;
this._telemetryService.publicLog('extensionsMessage', {
type, extensionId, extensionPointId, message
});
}
}
public $localShowMessage(severity: Severity, msg: string): void {
......@@ -175,7 +179,7 @@ export class MainProcessExtensionService extends AbstractExtensionService<Activa
users[usersLen++] = {
description: desc,
value: desc.contributes[extensionPoint.name],
collector: new ExtensionMessageCollector(messageHandler, desc.extensionFolderPath)
collector: new ExtensionMessageCollector(messageHandler, desc, extensionPoint.name)
};
}
}
......
......@@ -14,6 +14,7 @@ import paths = require('vs/base/common/paths');
import types = require('vs/base/common/types');
import uri from 'vs/base/common/uri';
import errors = require('vs/base/common/errors');
import * as browser from 'vs/base/browser/browser';
import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { Action } from 'vs/base/common/actions';
import { language, LANGUAGE_DEFAULT } from 'vs/base/common/platform';
......@@ -78,6 +79,7 @@ class StateChange {
encoding: boolean;
EOL: boolean;
tabFocusMode: boolean;
screenReaderMode: boolean;
metadata: boolean;
constructor() {
......@@ -87,6 +89,7 @@ class StateChange {
this.encoding = false;
this.EOL = false;
this.tabFocusMode = false;
this.screenReaderMode = false;
this.metadata = false;
}
......@@ -97,6 +100,7 @@ class StateChange {
this.encoding = this.encoding || other.encoding;
this.EOL = this.EOL || other.EOL;
this.tabFocusMode = this.tabFocusMode || other.tabFocusMode;
this.screenReaderMode = this.screenReaderMode || other.screenReaderMode;
this.metadata = this.metadata || other.metadata;
}
}
......@@ -108,6 +112,7 @@ interface StateDelta {
EOL?: string;
indentation?: string;
tabFocusMode?: boolean;
screenReaderMode?: boolean;
metadata?: string;
}
......@@ -130,6 +135,9 @@ class State {
private _tabFocusMode: boolean;
public get tabFocusMode(): boolean { return this._tabFocusMode; }
private _screenReaderMode: boolean;
public get screenReaderMode(): boolean { return this._screenReaderMode; }
private _metadata: string;
public get metadata(): string { return this._metadata; }
......@@ -139,6 +147,7 @@ class State {
this._encoding = null;
this._EOL = null;
this._tabFocusMode = false;
this._screenReaderMode = false;
this._metadata = null;
}
......@@ -188,6 +197,13 @@ class State {
e.tabFocusMode = true;
}
}
if (typeof update.screenReaderMode !== 'undefined') {
if (this._screenReaderMode !== update.screenReaderMode) {
this._screenReaderMode = update.screenReaderMode;
somethingChanged = true;
e.screenReaderMode = true;
}
}
if (typeof update.metadata !== 'undefined') {
if (this._metadata !== update.metadata) {
this._metadata = update.metadata;
......@@ -210,6 +226,7 @@ const nlsMultiSelection = nls.localize('multiSelection', "{0} selections");
const nlsEOLLF = nls.localize('endOfLineLineFeed', "LF");
const nlsEOLCRLF = nls.localize('endOfLineCarriageReturnLineFeed', "CRLF");
const nlsTabFocusMode = nls.localize('tabFocusModeEnabled', "Tab moves focus");
const nlsScreenReaderDetected = nls.localize('screenReaderDetected', "Screen reader detected");
function _setDisplay(el: HTMLElement, desiredValue: string): void {
if (el.style.display !== desiredValue) {
......@@ -228,6 +245,7 @@ export class EditorStatus implements IStatusbarItem {
private state: State;
private element: HTMLElement;
private tabFocusModeElement: HTMLElement;
private screenRedearModeElement: HTMLElement;
private indentationElement: HTMLElement;
private selectionElement: HTMLElement;
private encodingElement: HTMLElement;
......@@ -262,6 +280,10 @@ export class EditorStatus implements IStatusbarItem {
this.tabFocusModeElement.textContent = nlsTabFocusMode;
hide(this.tabFocusModeElement);
this.screenRedearModeElement = append(this.element, $('a.editor-status-screenreadermode.status-bar-info'));
this.screenRedearModeElement.textContent = nlsScreenReaderDetected;
hide(this.screenRedearModeElement);
this.selectionElement = append(this.element, $('a.editor-status-selection'));
this.selectionElement.title = nls.localize('gotoLine', "Go to Line");
this.selectionElement.onclick = () => this.onSelectionClick();
......@@ -306,8 +328,10 @@ export class EditorStatus implements IStatusbarItem {
this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged()),
this.untitledEditorService.onDidChangeEncoding(r => this.onResourceEncodingChange(r)),
this.textFileService.models.onModelEncodingChanged(e => this.onResourceEncodingChange(e.resource)),
TabFocus.onDidChangeTabFocus(e => this.onTabFocusModeChange())
TabFocus.onDidChangeTabFocus(e => this.onTabFocusModeChange()),
browser.onDidChangeAccessibilitySupport(() => this.onScreenReaderModeChange())
);
this.onScreenReaderModeChange();
return combinedDisposable(this.toDispose);
}
......@@ -341,6 +365,14 @@ export class EditorStatus implements IStatusbarItem {
}
}
if (changed.screenReaderMode) {
if (this.state.screenReaderMode && this.state.screenReaderMode === true) {
show(this.screenRedearModeElement);
} else {
hide(this.screenRedearModeElement);
}
}
if (changed.indentation) {
if (this.state.indentation) {
this.indentationElement.textContent = this.state.indentation;
......@@ -653,6 +685,12 @@ export class EditorStatus implements IStatusbarItem {
this.updateState(info);
}
private onScreenReaderModeChange(): void {
const info: StateDelta = { screenReaderMode: browser.getAccessibilitySupport() === browser.AccessibilitySupport.Enabled };
this.updateState(info);
}
private isActiveEditor(e: IBaseEditor): boolean {
const activeEditor = this.editorService.getActiveEditor();
......
......@@ -22,4 +22,7 @@
.monaco-workbench .editor-statusbar-item > .editor-status-tabfocusmode {
padding: 0 5px 0 5px;
}
.monaco-workbench .editor-statusbar-item > .editor-status-screenreadermode {
padding: 0 5px 0 5px;
}
\ No newline at end of file
......@@ -12,8 +12,6 @@ import URI from 'vs/base/common/uri';
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
import { IEditor, ICommonCodeEditor, IEditorViewState, IModel } from 'vs/editor/common/editorCommon';
import { IEditorInput, IEditorModel, IEditorOptions, ITextEditorOptions, IBaseResourceInput, Position, Verbosity } from 'vs/platform/editor/common/editor';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { IInstantiationService, IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
......@@ -818,25 +816,6 @@ export class TextDiffEditorOptions extends TextEditorOptions {
public autoRevealFirstChange: boolean;
}
/**
* Helper to return all opened editors with resources not belonging to the currently opened workspace.
*/
export function getOutOfWorkspaceEditorResources(editorGroupService: IEditorGroupService, contextService: IWorkspaceContextService): URI[] {
const resources: URI[] = [];
editorGroupService.getStacksModel().groups.forEach(group => {
const editors = group.getEditors();
editors.forEach(editor => {
const fileResource = toResource(editor, { supportSideBySide: true, filter: 'file' });
if (fileResource && !contextService.isInsideWorkspace(fileResource)) {
resources.push(fileResource);
}
});
});
return resources;
}
export interface IStacksModelChangeEvent {
group: IEditorGroup;
editor?: IEditorInput;
......
......@@ -38,10 +38,15 @@ gracefulFs.gracefulify(fs); // enable gracefulFs
export interface IWindowConfiguration extends ParsedArgs, IOpenFileRequest {
/**
* The physical keyboard is of ISO type (on OSX)
* The physical keyboard is of ISO type (on OSX).
*/
isISOKeyboard?: boolean;
/**
* Accessibility support is enabled.
*/
accessibilitySupportEnabled?: boolean;
appRoot: string;
execPath: string;
......@@ -65,6 +70,8 @@ export function startup(configuration: IWindowConfiguration): TPromise<void> {
KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged(configuration.isISOKeyboard);
browser.setAccessibilitySupport(configuration.accessibilitySupportEnabled ? browser.AccessibilitySupport.Enabled : browser.AccessibilitySupport.Disabled);
// Setup Intl
comparer.setFileNameComparer(new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }));
......
......@@ -296,6 +296,11 @@ export class ElectronWindow extends Themable {
KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged(isISOKeyboard);
});
// keyboard layout changed event
ipc.on('vscode:accessibilitySupportChanged', (event, accessibilitySupportEnabled: boolean) => {
browser.setAccessibilitySupport(accessibilitySupportEnabled ? browser.AccessibilitySupport.Enabled : browser.AccessibilitySupport.Disabled);
});
// Configuration changes
let previousConfiguredZoomLevel: number;
this.configurationService.onDidUpdateConfiguration(e => {
......
......@@ -48,7 +48,9 @@ export class MessagesCollector {
this._messages.push({
type: type,
message: message,
source: source
source: source,
extensionId: undefined,
extensionPointId: undefined
});
}
......
......@@ -37,7 +37,7 @@ import jsonContributionRegistry = require('vs/platform/jsonschemas/common/jsonCo
import { ExtensionsConfigurationSchema, ExtensionsConfigurationSchemaId } from 'vs/workbench/parts/extensions/common/extensionsFileTemplate';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeymapExtensions } from 'vs/workbench/parts/extensions/electron-browser/extensionsUtils';
import { KeymapExtensions, BetterMergeDisabled } from 'vs/workbench/parts/extensions/electron-browser/extensionsUtils';
import { adoptToGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
// Singletons
......@@ -48,6 +48,7 @@ registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService);
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
workbenchRegistry.registerWorkbenchContribution(StatusUpdater);
workbenchRegistry.registerWorkbenchContribution(KeymapExtensions);
workbenchRegistry.registerWorkbenchContribution(BetterMergeDisabled);
Registry.as<IOutputChannelRegistry>(OutputExtensions.OutputChannels)
.registerChannel(ExtensionsChannelId, ExtensionsLabel);
......
......@@ -6,18 +6,21 @@
'use strict';
import * as arrays from 'vs/base/common/arrays';
import * as nls from 'vs/nls';
import { localize } from 'vs/nls';
import Event, { chain, any, debounceEvent } from 'vs/base/common/event';
import { onUnexpectedError, canceled } from 'vs/base/common/errors';
import { TPromise } from 'vs/base/common/winjs.base';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IExtensionManagementService, ILocalExtension, IExtensionEnablementService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IChoiceService } from 'vs/platform/message/common/message';
import Severity from 'vs/base/common/severity';
import { IExtensionManagementService, ILocalExtension, IExtensionEnablementService, IExtensionTipsService, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IMessageService, Severity, IChoiceService } from 'vs/platform/message/common/message';
import { Action } from 'vs/base/common/actions';
import { BetterMergeDisabledNowKey, BetterMergeId, getIdAndVersionFromLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
export interface IExtensionStatus {
identifier: string;
......@@ -70,10 +73,10 @@ export class KeymapExtensions implements IWorkbenchContribution {
oldKeymaps: oldKeymaps.map(k => k.identifier)
};
this.telemetryService.publicLog('disableOtherKeymapsConfirmation', telemetryData);
const message = nls.localize('disableOtherKeymapsConfirmation', "Disable other keymaps to avoid conflicts between keybindings?");
const message = localize('disableOtherKeymapsConfirmation', "Disable other keymaps to avoid conflicts between keybindings?");
const options = [
nls.localize('yes', "Yes"),
nls.localize('no', "No")
localize('yes', "Yes"),
localize('no', "No")
];
return this.choiceService.choose(Severity.Info, message, options, 1, false)
.then<void>(value => {
......@@ -134,5 +137,47 @@ export function isKeymapExtension(tipsService: IExtensionTipsService, extension:
}
function stripVersion(id: string): string {
return id.replace(/-\d+\.\d+\.\d+$/, '');
return getIdAndVersionFromLocalExtensionId(id).id;
}
export class BetterMergeDisabled implements IWorkbenchContribution {
constructor(
@IStorageService storageService: IStorageService,
@IMessageService messageService: IMessageService,
@IExtensionService extensionService: IExtensionService,
@IExtensionManagementService extensionManagementService: IExtensionManagementService,
@ITelemetryService telemetryService: ITelemetryService,
) {
extensionService.onReady().then(() => {
if (storageService.getBoolean(BetterMergeDisabledNowKey, StorageScope.GLOBAL, false)) {
storageService.remove(BetterMergeDisabledNowKey, StorageScope.GLOBAL);
telemetryService.publicLog('betterMergeDisabled');
messageService.show(Severity.Info, {
message: localize('betterMergeDisabled', "The Better Merge extension is now built-in, the installed extension was disabled and can be uninstalled."),
actions: [
new Action('uninstall', localize('uninstall', "Uninstall"), null, true, () => {
telemetryService.publicLog('betterMergeUninstall', {
outcome: 'uninstall',
});
return extensionManagementService.getInstalled(LocalExtensionType.User).then(extensions => {
return Promise.all(extensions.filter(e => stripVersion(e.id) === BetterMergeId)
.map(e => extensionManagementService.uninstall(e, true)));
});
}),
new Action('later', localize('later', "Later"), null, true, () => {
telemetryService.publicLog('betterMergeUninstall', {
outcome: 'later',
});
return TPromise.as(true);
})
]
});
}
});
}
getId(): string {
return 'vs.extensions.betterMergeDisabled';
}
}
......@@ -21,7 +21,7 @@ import { IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen';
import { QuickOpenEntry, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel';
import { QuickOpenHandler, EditorQuickOpenEntry } from 'vs/workbench/browser/quickopen';
import { QueryBuilder } from 'vs/workbench/parts/search/common/searchQuery';
import { EditorInput, getOutOfWorkspaceEditorResources, IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor';
import { EditorInput, IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { IResourceInput } from 'vs/platform/editor/common/editor';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
......@@ -31,6 +31,7 @@ import { IQueryOptions, ISearchService, ISearchStats, ISearchQuery } from 'vs/pl
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IRange } from 'vs/editor/common/core/range';
import { getOutOfWorkspaceEditorResources } from "vs/workbench/parts/search/common/search";
export class FileQuickOpenModel extends QuickOpenModel {
......
......@@ -27,7 +27,6 @@ import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
import { Scope } from 'vs/workbench/common/memento';
import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { getOutOfWorkspaceEditorResources } from 'vs/workbench/common/editor';
import { FileChangeType, FileChangesEvent, IFileService } from 'vs/platform/files/common/files';
import { Viewlet } from 'vs/workbench/browser/viewlet';
import { Match, FileMatch, SearchModel, FileMatchOrMatch, IChangeEvent, ISearchWorkbenchService } from 'vs/workbench/parts/search/common/searchModel';
......@@ -61,6 +60,7 @@ import FileResultsNavigation from 'vs/workbench/browser/fileResultsNavigation';
import { attachListStyler } from 'vs/platform/theme/common/styler';
import { IOutputService } from 'vs/workbench/parts/output/common/output';
import { Color } from 'vs/base/common/color';
import { getOutOfWorkspaceEditorResources } from "vs/workbench/parts/search/common/search";
export class SearchViewlet extends Viewlet {
......
......@@ -12,6 +12,10 @@ import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions';
import { ISearchConfiguration } from 'vs/platform/search/common/search';
import glob = require('vs/base/common/glob');
import { SymbolInformation } from 'vs/editor/common/modes';
import { IEditorGroupService } from "vs/workbench/services/group/common/groupService";
import { IWorkspaceContextService } from "vs/platform/workspace/common/workspace";
import URI from "vs/base/common/uri";
import { toResource } from "vs/workbench/common/editor";
export interface IWorkspaceSymbolProvider {
provideWorkspaceSymbols(search: string): TPromise<SymbolInformation[]>;
......@@ -79,3 +83,22 @@ export interface IWorkbenchSearchConfiguration extends ISearchConfiguration {
useIgnoreFilesByDefault: boolean
};
}
/**
* Helper to return all opened editors with resources not belonging to the currently opened workspace.
*/
export function getOutOfWorkspaceEditorResources(editorGroupService: IEditorGroupService, contextService: IWorkspaceContextService): URI[] {
const resources: URI[] = [];
editorGroupService.getStacksModel().groups.forEach(group => {
const editors = group.getEditors();
editors.forEach(editor => {
const fileResource = toResource(editor, { supportSideBySide: true, filter: 'file' });
if (fileResource && !contextService.isInsideWorkspace(fileResource)) {
resources.push(fileResource);
}
});
});
return resources;
}
\ No newline at end of file
......@@ -15,6 +15,8 @@ import { ISnippetsService, ISnippet } from 'vs/workbench/parts/snippets/electron
import { IModeService } from 'vs/editor/common/services/modeService';
import { languagesExtPoint } from 'vs/editor/common/services/modeServiceImpl';
import { LanguageIdentifier } from 'vs/editor/common/modes';
import { SnippetParser, Marker, Placeholder, Variable, Text, walk } from 'vs/editor/contrib/snippet/common/snippetParser';
import { EditorSnippetVariableResolver } from "vs/editor/contrib/snippet/common/snippetVariables";
interface ISnippetsExtensionPoint {
language: string;
......@@ -79,19 +81,23 @@ export class MainProcessTextMateSnippet implements IWorkbenchContribution {
let modeId = snippet.language;
let languageIdentifier = this._modeService.getLanguageIdentifier(modeId);
if (languageIdentifier) {
readAndRegisterSnippets(this._snippetService, languageIdentifier, normalizedAbsolutePath, extensionName);
readAndRegisterSnippets(this._snippetService, languageIdentifier, normalizedAbsolutePath, extensionName, collector);
}
}
}
export function readAndRegisterSnippets(snippetService: ISnippetsService, languageIdentifier: LanguageIdentifier, filePath: string, extensionName?: string): TPromise<void> {
export function readAndRegisterSnippets(
snippetService: ISnippetsService, languageIdentifier: LanguageIdentifier, filePath: string,
extensionName?: string, collector?: ExtensionMessageCollector
): TPromise<void> {
return readFile(filePath).then(fileContents => {
let snippets = parseSnippetFile(fileContents.toString(), extensionName);
let snippets = parseSnippetFile(fileContents.toString(), extensionName, collector);
snippetService.registerSnippets(languageIdentifier.id, snippets, filePath);
});
}
function parseSnippetFile(snippetFileContent: string, extensionName?: string): ISnippet[] {
function parseSnippetFile(snippetFileContent: string, extensionName?: string, collector?: ExtensionMessageCollector): ISnippet[] {
let snippetsObj = parse(snippetFileContent);
if (!snippetsObj || typeof snippetsObj !== 'object') {
return [];
......@@ -102,21 +108,34 @@ function parseSnippetFile(snippetFileContent: string, extensionName?: string): I
let processSnippet = (snippet: any, name: string) => {
let prefix = snippet['prefix'];
let bodyStringOrArray = snippet['body'];
let body = <string | string[]>snippet['body'];
if (Array.isArray(bodyStringOrArray)) {
bodyStringOrArray = bodyStringOrArray.join('\n');
if (Array.isArray(body)) {
body = body.join('\n');
}
if (typeof prefix === 'string' && typeof bodyStringOrArray === 'string') {
result.push({
name,
extensionName,
prefix,
description: snippet['description'] || name,
codeSnippet: bodyStringOrArray
});
if (typeof prefix !== 'string' || typeof body !== 'string') {
return;
}
snippet = {
name,
extensionName,
prefix,
description: snippet['description'] || name,
codeSnippet: body
};
const didRewrite = _rewriteBogousVariables(snippet);
if (didRewrite && collector) {
collector.warn(nls.localize(
'badVariableUse',
"The \"{0}\"-snippet very likely confuses snippet-variables and snippet-placeholders. See https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax for more details.",
name
));
}
result.push(snippet);
};
topLevelProperties.forEach(topLevelProperty => {
......@@ -132,3 +151,49 @@ function parseSnippetFile(snippetFileContent: string, extensionName?: string): I
});
return result;
}
function _rewriteBogousVariables(snippet: ISnippet): boolean {
const marker = new SnippetParser(true, false).parse(snippet.codeSnippet, false);
let placeholders = new Map<string, number>();
let placeholderMax = 0;
walk(marker, candidate => {
if (candidate instanceof Placeholder) {
placeholderMax = Math.max(placeholderMax, Number(candidate.index));
}
return true;
});
function fixBogousVariables(marker: Marker): string {
if (marker instanceof Text) {
return SnippetParser.escape(marker.string);
} else if (marker instanceof Placeholder) {
if (marker.defaultValue.length > 0) {
return `\${${marker.index}:${marker.defaultValue.map(fixBogousVariables).join('')}}`;
} else {
return `\$${marker.index}`;
}
} else if (marker instanceof Variable) {
if (marker.defaultValue.length === 0 && !EditorSnippetVariableResolver.VariableNames[marker.name]) {
// a 'variable' without a default value and not being one of our supported
// variables is automatically turing into a placeholder. This is to restore
// a bug we had before. So `${foo}` becomes `${N:foo}`
let index = placeholders.has(marker.name) ? placeholders.get(marker.name) : ++placeholderMax;
placeholders.set(marker.name, index);
return `\${${index++}:${marker.name}}`;
} else if (marker.defaultValue.length > 0) {
return `\${${marker.name}:${marker.defaultValue.map(fixBogousVariables).join('')}}`;
} else {
return `\$${marker.name}`;
}
} else {
throw new Error('unexpected marker: ' + marker);
}
}
const placeholderCountBefore = placeholderMax;
snippet.codeSnippet = marker.map(fixBogousVariables).join('');
return placeholderCountBefore !== placeholderMax;
}
......@@ -61,10 +61,10 @@ export class EditorState {
return true; // always let API source win (e.g. "Go to definition" should add a history entry)
}
const liftedSelection = Selection.liftSelection(this._selection);
const liftedOtherSelection = Selection.liftSelection(other._selection);
const myLineNumber = Math.min(this._selection.selectionStartLineNumber, this._selection.positionLineNumber);
const otherLineNumber = Math.min(other._selection.selectionStartLineNumber, other._selection.positionLineNumber);
if (Math.abs(liftedSelection.getStartPosition().lineNumber - liftedOtherSelection.getStartPosition().lineNumber) < EditorState.EDITOR_SELECTION_THRESHOLD) {
if (Math.abs(myLineNumber - otherLineNumber) < EditorState.EDITOR_SELECTION_THRESHOLD) {
return false; // ignore selection changes in the range of EditorState.EDITOR_SELECTION_THRESHOLD lines
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册