mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-11 10:38:31 -05:00
Vscode merge (#4582)
* Merge from vscode 37cb23d3dd4f9433d56d4ba5ea3203580719a0bd * fix issues with merges * bump node version in azpipe * replace license headers * remove duplicate launch task * fix build errors * fix build errors * fix tslint issues * working through package and linux build issues * more work * wip * fix packaged builds * working through linux build errors * wip * wip * wip * fix mac and linux file limits * iterate linux pipeline * disable editor typing * revert series to parallel * remove optimize vscode from linux * fix linting issues * revert testing change * add work round for new node * readd packaging for extensions * fix issue with angular not resolving decorator dependencies
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-editor .accessibilityHelpWidget {
|
||||
padding: 10px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./accessibility';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
|
||||
import { renderFormattedText } from 'vs/base/browser/htmlContentRenderer';
|
||||
import { alert } from 'vs/base/browser/ui/aria/aria';
|
||||
import { Widget } from 'vs/base/browser/ui/widget';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorAction, EditorCommand, registerEditorAction, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import * as editorOptions from 'vs/editor/common/config/editorOptions';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode';
|
||||
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { contrastBorder, editorWidgetBackground, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
|
||||
|
||||
const CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE = new RawContextKey<boolean>('accessibilityHelpWidgetVisible', false);
|
||||
|
||||
class AccessibilityHelpController extends Disposable implements IEditorContribution {
|
||||
|
||||
private static readonly ID = 'editor.contrib.accessibilityHelpController';
|
||||
|
||||
public static get(editor: ICodeEditor): AccessibilityHelpController {
|
||||
return editor.getContribution<AccessibilityHelpController>(AccessibilityHelpController.ID);
|
||||
}
|
||||
|
||||
private _editor: ICodeEditor;
|
||||
private _widget: AccessibilityHelpWidget;
|
||||
|
||||
constructor(
|
||||
editor: ICodeEditor,
|
||||
@IInstantiationService instantiationService: IInstantiationService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._editor = editor;
|
||||
this._widget = this._register(instantiationService.createInstance(AccessibilityHelpWidget, this._editor));
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
return AccessibilityHelpController.ID;
|
||||
}
|
||||
|
||||
public show(): void {
|
||||
this._widget.show();
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
this._widget.hide();
|
||||
}
|
||||
}
|
||||
|
||||
class AccessibilityHelpWidget extends Widget implements IOverlayWidget {
|
||||
|
||||
private static readonly ID = 'editor.contrib.accessibilityHelpWidget';
|
||||
private static readonly WIDTH = 500;
|
||||
private static readonly HEIGHT = 300;
|
||||
|
||||
private _editor: ICodeEditor;
|
||||
private _domNode: FastDomNode<HTMLElement>;
|
||||
private _contentDomNode: FastDomNode<HTMLElement>;
|
||||
private _isVisible: boolean;
|
||||
private _isVisibleKey: IContextKey<boolean>;
|
||||
|
||||
constructor(
|
||||
editor: ICodeEditor,
|
||||
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
|
||||
@IKeybindingService private readonly _keybindingService: IKeybindingService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@IOpenerService private readonly _openerService: IOpenerService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._editor = editor;
|
||||
this._isVisibleKey = CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE.bindTo(this._contextKeyService);
|
||||
|
||||
this._domNode = createFastDomNode(document.createElement('div'));
|
||||
this._domNode.setClassName('accessibilityHelpWidget');
|
||||
this._domNode.setWidth(AccessibilityHelpWidget.WIDTH);
|
||||
this._domNode.setHeight(AccessibilityHelpWidget.HEIGHT);
|
||||
this._domNode.setDisplay('none');
|
||||
this._domNode.setAttribute('role', 'dialog');
|
||||
this._domNode.setAttribute('aria-hidden', 'true');
|
||||
|
||||
this._contentDomNode = createFastDomNode(document.createElement('div'));
|
||||
this._contentDomNode.setAttribute('role', 'document');
|
||||
this._domNode.appendChild(this._contentDomNode);
|
||||
|
||||
this._isVisible = false;
|
||||
|
||||
this._register(this._editor.onDidLayoutChange(() => {
|
||||
if (this._isVisible) {
|
||||
this._layout();
|
||||
}
|
||||
}));
|
||||
|
||||
// Intentionally not configurable!
|
||||
this._register(dom.addStandardDisposableListener(this._contentDomNode.domNode, 'keydown', (e) => {
|
||||
if (!this._isVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.equals(KeyMod.CtrlCmd | KeyCode.KEY_E)) {
|
||||
alert(nls.localize('emergencyConfOn', "Now changing the setting `editor.accessibilitySupport` to 'on'."));
|
||||
|
||||
this._configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER);
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
if (e.equals(KeyMod.CtrlCmd | KeyCode.KEY_H)) {
|
||||
alert(nls.localize('openingDocs', "Now opening the VS Code Accessibility documentation page."));
|
||||
|
||||
this._openerService.open(URI.parse('https://go.microsoft.com/fwlink/?linkid=851010'));
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}));
|
||||
|
||||
this.onblur(this._contentDomNode.domNode, () => {
|
||||
this.hide();
|
||||
});
|
||||
|
||||
this._editor.addOverlayWidget(this);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._editor.removeOverlayWidget(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
return AccessibilityHelpWidget.ID;
|
||||
}
|
||||
|
||||
public getDomNode(): HTMLElement {
|
||||
return this._domNode.domNode;
|
||||
}
|
||||
|
||||
public getPosition(): IOverlayWidgetPosition {
|
||||
return {
|
||||
preference: null
|
||||
};
|
||||
}
|
||||
|
||||
public show(): void {
|
||||
if (this._isVisible) {
|
||||
return;
|
||||
}
|
||||
this._isVisible = true;
|
||||
this._isVisibleKey.set(true);
|
||||
this._layout();
|
||||
this._domNode.setDisplay('block');
|
||||
this._domNode.setAttribute('aria-hidden', 'false');
|
||||
this._contentDomNode.domNode.tabIndex = 0;
|
||||
this._buildContent();
|
||||
this._contentDomNode.domNode.focus();
|
||||
}
|
||||
|
||||
private _descriptionForCommand(commandId: string, msg: string, noKbMsg: string): string {
|
||||
let kb = this._keybindingService.lookupKeybinding(commandId);
|
||||
if (kb) {
|
||||
return strings.format(msg, kb.getAriaLabel());
|
||||
}
|
||||
return strings.format(noKbMsg, commandId);
|
||||
}
|
||||
|
||||
private _buildContent() {
|
||||
let opts = this._editor.getConfiguration();
|
||||
let text = nls.localize('introMsg', "Thank you for trying out VS Code's accessibility options.");
|
||||
|
||||
text += '\n\n' + nls.localize('status', "Status:");
|
||||
|
||||
const configuredValue = this._configurationService.getValue<editorOptions.IEditorOptions>('editor').accessibilitySupport;
|
||||
const actualValue = opts.accessibilitySupport;
|
||||
|
||||
const emergencyTurnOnMessage = (
|
||||
platform.isMacintosh
|
||||
? nls.localize('changeConfigToOnMac', "To configure the editor to be permanently optimized for usage with a Screen Reader press Command+E now.")
|
||||
: nls.localize('changeConfigToOnWinLinux', "To configure the editor to be permanently optimized for usage with a Screen Reader press Control+E now.")
|
||||
);
|
||||
|
||||
switch (configuredValue) {
|
||||
case 'auto':
|
||||
switch (actualValue) {
|
||||
case AccessibilitySupport.Unknown:
|
||||
// Should never happen in VS Code
|
||||
text += '\n\n - ' + nls.localize('auto_unknown', "The editor is configured to use platform APIs to detect when a Screen Reader is attached, but the current runtime does not support this.");
|
||||
break;
|
||||
case AccessibilitySupport.Enabled:
|
||||
text += '\n\n - ' + nls.localize('auto_on', "The editor has automatically detected a Screen Reader is attached.");
|
||||
break;
|
||||
case AccessibilitySupport.Disabled:
|
||||
text += '\n\n - ' + nls.localize('auto_off', "The editor is configured to automatically detect when a Screen Reader is attached, which is not the case at this time.");
|
||||
text += ' ' + emergencyTurnOnMessage;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'on':
|
||||
text += '\n\n - ' + nls.localize('configuredOn', "The editor is configured to be permanently optimized for usage with a Screen Reader - you can change this by editing the setting `editor.accessibilitySupport`.");
|
||||
break;
|
||||
case 'off':
|
||||
text += '\n\n - ' + nls.localize('configuredOff', "The editor is configured to never be optimized for usage with a Screen Reader.");
|
||||
text += ' ' + emergencyTurnOnMessage;
|
||||
break;
|
||||
}
|
||||
|
||||
const NLS_TAB_FOCUS_MODE_ON = nls.localize('tabFocusModeOnMsg', "Pressing Tab in the current editor will move focus to the next focusable element. Toggle this behavior by pressing {0}.");
|
||||
const NLS_TAB_FOCUS_MODE_ON_NO_KB = nls.localize('tabFocusModeOnMsgNoKb', "Pressing Tab in the current editor will move focus to the next focusable element. The command {0} is currently not triggerable by a keybinding.");
|
||||
const NLS_TAB_FOCUS_MODE_OFF = nls.localize('tabFocusModeOffMsg', "Pressing Tab in the current editor will insert the tab character. Toggle this behavior by pressing {0}.");
|
||||
const NLS_TAB_FOCUS_MODE_OFF_NO_KB = nls.localize('tabFocusModeOffMsgNoKb', "Pressing Tab in the current editor will insert the tab character. The command {0} is currently not triggerable by a keybinding.");
|
||||
|
||||
if (opts.tabFocusMode) {
|
||||
text += '\n\n - ' + this._descriptionForCommand(ToggleTabFocusModeAction.ID, NLS_TAB_FOCUS_MODE_ON, NLS_TAB_FOCUS_MODE_ON_NO_KB);
|
||||
} else {
|
||||
text += '\n\n - ' + this._descriptionForCommand(ToggleTabFocusModeAction.ID, NLS_TAB_FOCUS_MODE_OFF, NLS_TAB_FOCUS_MODE_OFF_NO_KB);
|
||||
}
|
||||
|
||||
const openDocMessage = (
|
||||
platform.isMacintosh
|
||||
? nls.localize('openDocMac', "Press Command+H now to open a browser window with more VS Code information related to Accessibility.")
|
||||
: nls.localize('openDocWinLinux', "Press Control+H now to open a browser window with more VS Code information related to Accessibility.")
|
||||
);
|
||||
|
||||
text += '\n\n' + openDocMessage;
|
||||
|
||||
text += '\n\n' + nls.localize('outroMsg', "You can dismiss this tooltip and return to the editor by pressing Escape or Shift+Escape.");
|
||||
|
||||
this._contentDomNode.domNode.appendChild(renderFormattedText(text));
|
||||
// Per https://www.w3.org/TR/wai-aria/roles#document, Authors SHOULD provide a title or label for documents
|
||||
this._contentDomNode.domNode.setAttribute('aria-label', text);
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
if (!this._isVisible) {
|
||||
return;
|
||||
}
|
||||
this._isVisible = false;
|
||||
this._isVisibleKey.reset();
|
||||
this._domNode.setDisplay('none');
|
||||
this._domNode.setAttribute('aria-hidden', 'true');
|
||||
this._contentDomNode.domNode.tabIndex = -1;
|
||||
dom.clearNode(this._contentDomNode.domNode);
|
||||
|
||||
this._editor.focus();
|
||||
}
|
||||
|
||||
private _layout(): void {
|
||||
let editorLayout = this._editor.getLayoutInfo();
|
||||
|
||||
let top = Math.round((editorLayout.height - AccessibilityHelpWidget.HEIGHT) / 2);
|
||||
this._domNode.setTop(top);
|
||||
|
||||
let left = Math.round((editorLayout.width - AccessibilityHelpWidget.WIDTH) / 2);
|
||||
this._domNode.setLeft(left);
|
||||
}
|
||||
}
|
||||
|
||||
class ShowAccessibilityHelpAction extends EditorAction {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'editor.action.showAccessibilityHelp',
|
||||
label: nls.localize('ShowAccessibilityHelpAction', "Show Accessibility Help"),
|
||||
alias: 'Show Accessibility Help',
|
||||
precondition: null,
|
||||
kbOpts: {
|
||||
kbExpr: EditorContextKeys.focus,
|
||||
primary: KeyMod.Alt | KeyCode.F1,
|
||||
weight: KeybindingWeight.EditorContrib
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
|
||||
let controller = AccessibilityHelpController.get(editor);
|
||||
if (controller) {
|
||||
controller.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorContribution(AccessibilityHelpController);
|
||||
registerEditorAction(ShowAccessibilityHelpAction);
|
||||
|
||||
const AccessibilityHelpCommand = EditorCommand.bindToContribution<AccessibilityHelpController>(AccessibilityHelpController.get);
|
||||
|
||||
registerEditorCommand(new AccessibilityHelpCommand({
|
||||
id: 'closeAccessibilityHelp',
|
||||
precondition: CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE,
|
||||
handler: x => x.hide(),
|
||||
kbOpts: {
|
||||
weight: KeybindingWeight.EditorContrib + 100,
|
||||
kbExpr: EditorContextKeys.focus,
|
||||
primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape]
|
||||
}
|
||||
}));
|
||||
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
const widgetBackground = theme.getColor(editorWidgetBackground);
|
||||
if (widgetBackground) {
|
||||
collector.addRule(`.monaco-editor .accessibilityHelpWidget { background-color: ${widgetBackground}; }`);
|
||||
}
|
||||
|
||||
const widgetShadowColor = theme.getColor(widgetShadow);
|
||||
if (widgetShadowColor) {
|
||||
collector.addRule(`.monaco-editor .accessibilityHelpWidget { box-shadow: 0 2px 8px ${widgetShadowColor}; }`);
|
||||
}
|
||||
|
||||
const hcBorder = theme.getColor(contrastBorder);
|
||||
if (hcBorder) {
|
||||
collector.addRule(`.monaco-editor .accessibilityHelpWidget { border: 2px solid ${hcBorder}; }`);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import './menuPreventer';
|
||||
import './accessibility/accessibility';
|
||||
import './inspectKeybindings';
|
||||
import './largeFileOptimizations';
|
||||
import './selectionClipboard';
|
||||
import './inspectTMScopes/inspectTMScopes';
|
||||
import './toggleMinimap';
|
||||
import './toggleMultiCursorModifier';
|
||||
import './toggleRenderControlCharacter';
|
||||
import './toggleRenderWhitespace';
|
||||
import './toggleWordWrap';
|
||||
import './workbenchReferenceSearch';
|
||||
@@ -0,0 +1,32 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorAction, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IUntitledResourceInput } from 'vs/workbench/common/editor';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
||||
class InspectKeyMap extends EditorAction {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.inspectKeyMappings',
|
||||
label: nls.localize('workbench.action.inspectKeyMap', "Developer: Inspect Key Mappings"),
|
||||
alias: 'Developer: Inspect Key Mappings',
|
||||
precondition: null
|
||||
});
|
||||
}
|
||||
|
||||
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
|
||||
const keybindingService = accessor.get(IKeybindingService);
|
||||
const editorService = accessor.get(IEditorService);
|
||||
|
||||
editorService.openEditor({ contents: keybindingService._dumpDebugInfo(), options: { pinned: true } } as IUntitledResourceInput);
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorAction(InspectKeyMap);
|
||||
@@ -0,0 +1,38 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.tm-inspect-widget {
|
||||
z-index: 50;
|
||||
user-select: text;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tm-token {
|
||||
font-family: var(--monaco-monospace-font);
|
||||
}
|
||||
|
||||
.tm-metadata-separator {
|
||||
height: 1px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.tm-token-length {
|
||||
font-weight: normal;
|
||||
font-size: 60%;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.tm-metadata-table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tm-metadata-value {
|
||||
font-family: var(--monaco-monospace-font);
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.tm-theme-selector {
|
||||
font-family: var(--monaco-monospace-font);
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./inspectTMScopes';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { escape } from 'vs/base/common/strings';
|
||||
import { ContentWidgetPositionPreference, IActiveCodeEditor, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { FontStyle, LanguageIdentifier, StandardTokenType, TokenMetadata, TokenizationRegistry } from 'vs/editor/common/modes';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { editorHoverBackground, editorHoverBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { HIGH_CONTRAST, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { findMatchingThemeRule } from 'vs/workbench/services/textMate/common/TMHelper';
|
||||
import { ITextMateService, IGrammar, IToken, StackElement } from 'vs/workbench/services/textMate/common/textMateService';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
|
||||
class InspectTMScopesController extends Disposable implements IEditorContribution {
|
||||
|
||||
private static readonly ID = 'editor.contrib.inspectTMScopes';
|
||||
|
||||
public static get(editor: ICodeEditor): InspectTMScopesController {
|
||||
return editor.getContribution<InspectTMScopesController>(InspectTMScopesController.ID);
|
||||
}
|
||||
|
||||
private _editor: ICodeEditor;
|
||||
private _textMateService: ITextMateService;
|
||||
private _themeService: IWorkbenchThemeService;
|
||||
private _modeService: IModeService;
|
||||
private _notificationService: INotificationService;
|
||||
private _widget: InspectTMScopesWidget | null;
|
||||
|
||||
constructor(
|
||||
editor: ICodeEditor,
|
||||
@ITextMateService textMateService: ITextMateService,
|
||||
@IModeService modeService: IModeService,
|
||||
@IWorkbenchThemeService themeService: IWorkbenchThemeService,
|
||||
@INotificationService notificationService: INotificationService
|
||||
) {
|
||||
super();
|
||||
this._editor = editor;
|
||||
this._textMateService = textMateService;
|
||||
this._themeService = themeService;
|
||||
this._modeService = modeService;
|
||||
this._notificationService = notificationService;
|
||||
this._widget = null;
|
||||
|
||||
this._register(this._editor.onDidChangeModel((e) => this.stop()));
|
||||
this._register(this._editor.onDidChangeModelLanguage((e) => this.stop()));
|
||||
this._register(this._editor.onKeyUp((e) => e.keyCode === KeyCode.Escape && this.stop()));
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
return InspectTMScopesController.ID;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.stop();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
public launch(): void {
|
||||
if (this._widget) {
|
||||
return;
|
||||
}
|
||||
if (!this._editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
this._widget = new InspectTMScopesWidget(this._editor, this._textMateService, this._modeService, this._themeService, this._notificationService);
|
||||
}
|
||||
|
||||
public stop(): void {
|
||||
if (this._widget) {
|
||||
this._widget.dispose();
|
||||
this._widget = null;
|
||||
}
|
||||
}
|
||||
|
||||
public toggle(): void {
|
||||
if (!this._widget) {
|
||||
this.launch();
|
||||
} else {
|
||||
this.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class InspectTMScopes extends EditorAction {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'editor.action.inspectTMScopes',
|
||||
label: nls.localize('inspectTMScopes', "Developer: Inspect TM Scopes"),
|
||||
alias: 'Developer: Inspect TM Scopes',
|
||||
precondition: null
|
||||
});
|
||||
}
|
||||
|
||||
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
|
||||
let controller = InspectTMScopesController.get(editor);
|
||||
if (controller) {
|
||||
controller.toggle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface ICompleteLineTokenization {
|
||||
startState: StackElement | null;
|
||||
tokens1: IToken[];
|
||||
tokens2: Uint32Array;
|
||||
endState: StackElement;
|
||||
}
|
||||
|
||||
interface IDecodedMetadata {
|
||||
languageIdentifier: LanguageIdentifier;
|
||||
tokenType: StandardTokenType;
|
||||
fontStyle: FontStyle;
|
||||
foreground: Color;
|
||||
background: Color;
|
||||
}
|
||||
|
||||
function renderTokenText(tokenText: string): string {
|
||||
if (tokenText.length > 40) {
|
||||
tokenText = tokenText.substr(0, 20) + '…' + tokenText.substr(tokenText.length - 20);
|
||||
}
|
||||
let result: string = '';
|
||||
for (let charIndex = 0, len = tokenText.length; charIndex < len; charIndex++) {
|
||||
let charCode = tokenText.charCodeAt(charIndex);
|
||||
switch (charCode) {
|
||||
case CharCode.Tab:
|
||||
result += '→';
|
||||
break;
|
||||
|
||||
case CharCode.Space:
|
||||
result += '·';
|
||||
break;
|
||||
|
||||
case CharCode.LessThan:
|
||||
result += '<';
|
||||
break;
|
||||
|
||||
case CharCode.GreaterThan:
|
||||
result += '>';
|
||||
break;
|
||||
|
||||
case CharCode.Ampersand:
|
||||
result += '&';
|
||||
break;
|
||||
|
||||
default:
|
||||
result += String.fromCharCode(charCode);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
class InspectTMScopesWidget extends Disposable implements IContentWidget {
|
||||
|
||||
private static readonly _ID = 'editor.contrib.inspectTMScopesWidget';
|
||||
|
||||
// Editor.IContentWidget.allowEditorOverflow
|
||||
public readonly allowEditorOverflow = true;
|
||||
|
||||
private _isDisposed: boolean;
|
||||
private readonly _editor: IActiveCodeEditor;
|
||||
private readonly _modeService: IModeService;
|
||||
private readonly _themeService: IWorkbenchThemeService;
|
||||
private readonly _notificationService: INotificationService;
|
||||
private readonly _model: ITextModel;
|
||||
private readonly _domNode: HTMLElement;
|
||||
private readonly _grammar: Promise<IGrammar>;
|
||||
|
||||
constructor(
|
||||
editor: IActiveCodeEditor,
|
||||
textMateService: ITextMateService,
|
||||
modeService: IModeService,
|
||||
themeService: IWorkbenchThemeService,
|
||||
notificationService: INotificationService
|
||||
) {
|
||||
super();
|
||||
this._isDisposed = false;
|
||||
this._editor = editor;
|
||||
this._modeService = modeService;
|
||||
this._themeService = themeService;
|
||||
this._notificationService = notificationService;
|
||||
this._model = this._editor.getModel();
|
||||
this._domNode = document.createElement('div');
|
||||
this._domNode.className = 'tm-inspect-widget';
|
||||
this._grammar = textMateService.createGrammar(this._model.getLanguageIdentifier().language);
|
||||
this._beginCompute(this._editor.getPosition());
|
||||
this._register(this._editor.onDidChangeCursorPosition((e) => this._beginCompute(this._editor.getPosition())));
|
||||
this._editor.addContentWidget(this);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._isDisposed = true;
|
||||
this._editor.removeContentWidget(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
return InspectTMScopesWidget._ID;
|
||||
}
|
||||
|
||||
private _beginCompute(position: Position): void {
|
||||
dom.clearNode(this._domNode);
|
||||
this._domNode.appendChild(document.createTextNode(nls.localize('inspectTMScopesWidget.loading', "Loading...")));
|
||||
this._grammar.then(
|
||||
(grammar) => this._compute(grammar, position),
|
||||
(err) => {
|
||||
this._notificationService.warn(err);
|
||||
setTimeout(() => {
|
||||
InspectTMScopesController.get(this._editor).stop();
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private _compute(grammar: IGrammar, position: Position): void {
|
||||
if (this._isDisposed) {
|
||||
return;
|
||||
}
|
||||
let data = this._getTokensAtLine(grammar, position.lineNumber);
|
||||
|
||||
let token1Index = 0;
|
||||
for (let i = data.tokens1.length - 1; i >= 0; i--) {
|
||||
let t = data.tokens1[i];
|
||||
if (position.column - 1 >= t.startIndex) {
|
||||
token1Index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let token2Index = 0;
|
||||
for (let i = (data.tokens2.length >>> 1); i >= 0; i--) {
|
||||
if (position.column - 1 >= data.tokens2[(i << 1)]) {
|
||||
token2Index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let result = '';
|
||||
|
||||
let tokenStartIndex = data.tokens1[token1Index].startIndex;
|
||||
let tokenEndIndex = data.tokens1[token1Index].endIndex;
|
||||
let tokenText = this._model.getLineContent(position.lineNumber).substring(tokenStartIndex, tokenEndIndex);
|
||||
result += `<h2 class="tm-token">${renderTokenText(tokenText)}<span class="tm-token-length">(${tokenText.length} ${tokenText.length === 1 ? 'char' : 'chars'})</span></h2>`;
|
||||
|
||||
result += `<hr class="tm-metadata-separator" style="clear:both"/>`;
|
||||
|
||||
let metadata = this._decodeMetadata(data.tokens2[(token2Index << 1) + 1]);
|
||||
result += `<table class="tm-metadata-table"><tbody>`;
|
||||
result += `<tr><td class="tm-metadata-key">language</td><td class="tm-metadata-value">${escape(metadata.languageIdentifier.language)}</td></tr>`;
|
||||
result += `<tr><td class="tm-metadata-key">token type</td><td class="tm-metadata-value">${this._tokenTypeToString(metadata.tokenType)}</td></tr>`;
|
||||
result += `<tr><td class="tm-metadata-key">font style</td><td class="tm-metadata-value">${this._fontStyleToString(metadata.fontStyle)}</td></tr>`;
|
||||
result += `<tr><td class="tm-metadata-key">foreground</td><td class="tm-metadata-value">${Color.Format.CSS.formatHexA(metadata.foreground)}</td></tr>`;
|
||||
result += `<tr><td class="tm-metadata-key">background</td><td class="tm-metadata-value">${Color.Format.CSS.formatHexA(metadata.background)}</td></tr>`;
|
||||
if (metadata.background.isOpaque() && metadata.foreground.isOpaque()) {
|
||||
result += `<tr><td class="tm-metadata-key">contrast ratio</td><td class="tm-metadata-value">${metadata.background.getContrastRatio(metadata.foreground).toFixed(2)}</td></tr>`;
|
||||
} else {
|
||||
result += '<tr><td class="tm-metadata-key">Contrast ratio cannot be precise for colors that use transparency</td><td class="tm-metadata-value"></td></tr>';
|
||||
}
|
||||
result += `</tbody></table>`;
|
||||
|
||||
let theme = this._themeService.getColorTheme();
|
||||
result += `<hr class="tm-metadata-separator"/>`;
|
||||
let matchingRule = findMatchingThemeRule(theme, data.tokens1[token1Index].scopes, false);
|
||||
if (matchingRule) {
|
||||
result += `<code class="tm-theme-selector">${matchingRule.rawSelector}\n${JSON.stringify(matchingRule.settings, null, '\t')}</code>`;
|
||||
} else {
|
||||
result += `<span class="tm-theme-selector">No theme selector.</span>`;
|
||||
}
|
||||
|
||||
result += `<hr class="tm-metadata-separator"/>`;
|
||||
|
||||
result += `<ul>`;
|
||||
for (let i = data.tokens1[token1Index].scopes.length - 1; i >= 0; i--) {
|
||||
result += `<li>${escape(data.tokens1[token1Index].scopes[i])}</li>`;
|
||||
}
|
||||
result += `</ul>`;
|
||||
|
||||
|
||||
this._domNode.innerHTML = result;
|
||||
this._editor.layoutContentWidget(this);
|
||||
}
|
||||
|
||||
private _decodeMetadata(metadata: number): IDecodedMetadata {
|
||||
let colorMap = TokenizationRegistry.getColorMap()!;
|
||||
let languageId = TokenMetadata.getLanguageId(metadata);
|
||||
let tokenType = TokenMetadata.getTokenType(metadata);
|
||||
let fontStyle = TokenMetadata.getFontStyle(metadata);
|
||||
let foreground = TokenMetadata.getForeground(metadata);
|
||||
let background = TokenMetadata.getBackground(metadata);
|
||||
return {
|
||||
languageIdentifier: this._modeService.getLanguageIdentifier(languageId)!,
|
||||
tokenType: tokenType,
|
||||
fontStyle: fontStyle,
|
||||
foreground: colorMap[foreground],
|
||||
background: colorMap[background]
|
||||
};
|
||||
}
|
||||
|
||||
private _tokenTypeToString(tokenType: StandardTokenType): string {
|
||||
switch (tokenType) {
|
||||
case StandardTokenType.Other: return 'Other';
|
||||
case StandardTokenType.Comment: return 'Comment';
|
||||
case StandardTokenType.String: return 'String';
|
||||
case StandardTokenType.RegEx: return 'RegEx';
|
||||
}
|
||||
return '??';
|
||||
}
|
||||
|
||||
private _fontStyleToString(fontStyle: FontStyle): string {
|
||||
let r = '';
|
||||
if (fontStyle & FontStyle.Italic) {
|
||||
r += 'italic ';
|
||||
}
|
||||
if (fontStyle & FontStyle.Bold) {
|
||||
r += 'bold ';
|
||||
}
|
||||
if (fontStyle & FontStyle.Underline) {
|
||||
r += 'underline ';
|
||||
}
|
||||
if (r.length === 0) {
|
||||
r = '---';
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private _getTokensAtLine(grammar: IGrammar, lineNumber: number): ICompleteLineTokenization {
|
||||
let stateBeforeLine = this._getStateBeforeLine(grammar, lineNumber);
|
||||
|
||||
let tokenizationResult1 = grammar.tokenizeLine(this._model.getLineContent(lineNumber), stateBeforeLine);
|
||||
let tokenizationResult2 = grammar.tokenizeLine2(this._model.getLineContent(lineNumber), stateBeforeLine);
|
||||
|
||||
return {
|
||||
startState: stateBeforeLine,
|
||||
tokens1: tokenizationResult1.tokens,
|
||||
tokens2: tokenizationResult2.tokens,
|
||||
endState: tokenizationResult1.ruleStack
|
||||
};
|
||||
}
|
||||
|
||||
private _getStateBeforeLine(grammar: IGrammar, lineNumber: number): StackElement | null {
|
||||
let state: StackElement | null = null;
|
||||
|
||||
for (let i = 1; i < lineNumber; i++) {
|
||||
let tokenizationResult = grammar.tokenizeLine(this._model.getLineContent(i), state);
|
||||
state = tokenizationResult.ruleStack;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
public getDomNode(): HTMLElement {
|
||||
return this._domNode;
|
||||
}
|
||||
|
||||
public getPosition(): IContentWidgetPosition {
|
||||
return {
|
||||
position: this._editor.getPosition(),
|
||||
preference: [ContentWidgetPositionPreference.BELOW, ContentWidgetPositionPreference.ABOVE]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorContribution(InspectTMScopesController);
|
||||
registerEditorAction(InspectTMScopes);
|
||||
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
const border = theme.getColor(editorHoverBorder);
|
||||
if (border) {
|
||||
let borderWidth = theme.type === HIGH_CONTRAST ? 2 : 1;
|
||||
collector.addRule(`.monaco-editor .tm-inspect-widget { border: ${borderWidth}px solid ${border}; }`);
|
||||
collector.addRule(`.monaco-editor .tm-inspect-widget .tm-metadata-separator { background-color: ${border}; }`);
|
||||
}
|
||||
const background = theme.getColor(editorHoverBackground);
|
||||
if (background) {
|
||||
collector.addRule(`.monaco-editor .tm-inspect-widget { background-color: ${background}; }`);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,603 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { ParseError, parse } from 'vs/base/common/json';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { LanguageIdentifier } from 'vs/editor/common/modes';
|
||||
import { CharacterPair, CommentRule, FoldingRules, IAutoClosingPair, IAutoClosingPairConditional, IndentationRule, LanguageConfiguration } from 'vs/editor/common/modes/languageConfiguration';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ITextMateService } from 'vs/workbench/services/textMate/common/textMateService';
|
||||
|
||||
interface IRegExp {
|
||||
pattern: string;
|
||||
flags?: string;
|
||||
}
|
||||
|
||||
interface IIndentationRules {
|
||||
decreaseIndentPattern: string | IRegExp;
|
||||
increaseIndentPattern: string | IRegExp;
|
||||
indentNextLinePattern?: string | IRegExp;
|
||||
unIndentedLinePattern?: string | IRegExp;
|
||||
}
|
||||
|
||||
interface ILanguageConfiguration {
|
||||
comments?: CommentRule;
|
||||
brackets?: CharacterPair[];
|
||||
autoClosingPairs?: Array<CharacterPair | IAutoClosingPairConditional>;
|
||||
surroundingPairs?: Array<CharacterPair | IAutoClosingPair>;
|
||||
wordPattern?: string | IRegExp;
|
||||
indentationRules?: IIndentationRules;
|
||||
folding?: FoldingRules;
|
||||
autoCloseBefore?: string;
|
||||
}
|
||||
|
||||
function isStringArr(something: string[] | null): something is string[] {
|
||||
if (!Array.isArray(something)) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0, len = something.length; i < len; i++) {
|
||||
if (typeof something[i] !== 'string') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
function isCharacterPair(something: CharacterPair | null): boolean {
|
||||
return (
|
||||
isStringArr(something)
|
||||
&& something.length === 2
|
||||
);
|
||||
}
|
||||
|
||||
export class LanguageConfigurationFileHandler {
|
||||
|
||||
private _done: boolean[];
|
||||
|
||||
constructor(
|
||||
@ITextMateService textMateService: ITextMateService,
|
||||
@IModeService private readonly _modeService: IModeService,
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
@IExtensionService private readonly _extensionService: IExtensionService
|
||||
) {
|
||||
this._done = [];
|
||||
|
||||
// Listen for hints that a language configuration is needed/usefull and then load it once
|
||||
this._modeService.onDidCreateMode((mode) => {
|
||||
const languageIdentifier = mode.getLanguageIdentifier();
|
||||
// Modes can be instantiated before the extension points have finished registering
|
||||
this._extensionService.whenInstalledExtensionsRegistered().then(() => {
|
||||
this._loadConfigurationsForMode(languageIdentifier);
|
||||
});
|
||||
});
|
||||
textMateService.onDidEncounterLanguage((languageId) => {
|
||||
this._loadConfigurationsForMode(this._modeService.getLanguageIdentifier(languageId)!);
|
||||
});
|
||||
}
|
||||
|
||||
private _loadConfigurationsForMode(languageIdentifier: LanguageIdentifier): void {
|
||||
if (this._done[languageIdentifier.id]) {
|
||||
return;
|
||||
}
|
||||
this._done[languageIdentifier.id] = true;
|
||||
|
||||
let configurationFiles = this._modeService.getConfigurationFiles(languageIdentifier.language);
|
||||
configurationFiles.forEach((configFileLocation) => this._handleConfigFile(languageIdentifier, configFileLocation));
|
||||
}
|
||||
|
||||
private _handleConfigFile(languageIdentifier: LanguageIdentifier, configFileLocation: URI): void {
|
||||
this._fileService.resolveContent(configFileLocation, { encoding: 'utf8' }).then((contents) => {
|
||||
const errors: ParseError[] = [];
|
||||
const configuration = <ILanguageConfiguration>parse(contents.value.toString(), errors);
|
||||
if (errors.length) {
|
||||
console.error(nls.localize('parseErrors', "Errors parsing {0}: {1}", configFileLocation.toString(), errors.join('\n')));
|
||||
}
|
||||
this._handleConfig(languageIdentifier, configuration);
|
||||
}, (err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
private _extractValidCommentRule(languageIdentifier: LanguageIdentifier, configuration: ILanguageConfiguration): CommentRule | null {
|
||||
const source = configuration.comments;
|
||||
if (typeof source === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
if (!types.isObject(source)) {
|
||||
console.warn(`[${languageIdentifier.language}]: language configuration: expected \`comments\` to be an object.`);
|
||||
return null;
|
||||
}
|
||||
|
||||
let result: CommentRule | null = null;
|
||||
if (typeof source.lineComment !== 'undefined') {
|
||||
if (typeof source.lineComment !== 'string') {
|
||||
console.warn(`[${languageIdentifier.language}]: language configuration: expected \`comments.lineComment\` to be a string.`);
|
||||
} else {
|
||||
result = result || {};
|
||||
result.lineComment = source.lineComment;
|
||||
}
|
||||
}
|
||||
if (typeof source.blockComment !== 'undefined') {
|
||||
if (!isCharacterPair(source.blockComment)) {
|
||||
console.warn(`[${languageIdentifier.language}]: language configuration: expected \`comments.blockComment\` to be an array of two strings.`);
|
||||
} else {
|
||||
result = result || {};
|
||||
result.blockComment = source.blockComment;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private _extractValidBrackets(languageIdentifier: LanguageIdentifier, configuration: ILanguageConfiguration): CharacterPair[] | null {
|
||||
const source = configuration.brackets;
|
||||
if (typeof source === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
if (!Array.isArray(source)) {
|
||||
console.warn(`[${languageIdentifier.language}]: language configuration: expected \`brackets\` to be an array.`);
|
||||
return null;
|
||||
}
|
||||
|
||||
let result: CharacterPair[] | null = null;
|
||||
for (let i = 0, len = source.length; i < len; i++) {
|
||||
const pair = source[i];
|
||||
if (!isCharacterPair(pair)) {
|
||||
console.warn(`[${languageIdentifier.language}]: language configuration: expected \`brackets[${i}]\` to be an array of two strings.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
result = result || [];
|
||||
result.push(pair);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private _extractValidAutoClosingPairs(languageIdentifier: LanguageIdentifier, configuration: ILanguageConfiguration): IAutoClosingPairConditional[] | null {
|
||||
const source = configuration.autoClosingPairs;
|
||||
if (typeof source === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
if (!Array.isArray(source)) {
|
||||
console.warn(`[${languageIdentifier.language}]: language configuration: expected \`autoClosingPairs\` to be an array.`);
|
||||
return null;
|
||||
}
|
||||
|
||||
let result: IAutoClosingPairConditional[] | null = null;
|
||||
for (let i = 0, len = source.length; i < len; i++) {
|
||||
const pair = source[i];
|
||||
if (Array.isArray(pair)) {
|
||||
if (!isCharacterPair(pair)) {
|
||||
console.warn(`[${languageIdentifier.language}]: language configuration: expected \`autoClosingPairs[${i}]\` to be an array of two strings or an object.`);
|
||||
continue;
|
||||
}
|
||||
result = result || [];
|
||||
result.push({ open: pair[0], close: pair[1] });
|
||||
} else {
|
||||
if (!types.isObject(pair)) {
|
||||
console.warn(`[${languageIdentifier.language}]: language configuration: expected \`autoClosingPairs[${i}]\` to be an array of two strings or an object.`);
|
||||
continue;
|
||||
}
|
||||
if (typeof pair.open !== 'string') {
|
||||
console.warn(`[${languageIdentifier.language}]: language configuration: expected \`autoClosingPairs[${i}].open\` to be a string.`);
|
||||
continue;
|
||||
}
|
||||
if (typeof pair.close !== 'string') {
|
||||
console.warn(`[${languageIdentifier.language}]: language configuration: expected \`autoClosingPairs[${i}].close\` to be a string.`);
|
||||
continue;
|
||||
}
|
||||
if (typeof pair.notIn !== 'undefined') {
|
||||
if (!isStringArr(pair.notIn)) {
|
||||
console.warn(`[${languageIdentifier.language}]: language configuration: expected \`autoClosingPairs[${i}].notIn\` to be a string array.`);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
result = result || [];
|
||||
result.push({ open: pair.open, close: pair.close, notIn: pair.notIn });
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private _extractValidSurroundingPairs(languageIdentifier: LanguageIdentifier, configuration: ILanguageConfiguration): IAutoClosingPair[] | null {
|
||||
const source = configuration.surroundingPairs;
|
||||
if (typeof source === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
if (!Array.isArray(source)) {
|
||||
console.warn(`[${languageIdentifier.language}]: language configuration: expected \`surroundingPairs\` to be an array.`);
|
||||
return null;
|
||||
}
|
||||
|
||||
let result: IAutoClosingPair[] | null = null;
|
||||
for (let i = 0, len = source.length; i < len; i++) {
|
||||
const pair = source[i];
|
||||
if (Array.isArray(pair)) {
|
||||
if (!isCharacterPair(pair)) {
|
||||
console.warn(`[${languageIdentifier.language}]: language configuration: expected \`surroundingPairs[${i}]\` to be an array of two strings or an object.`);
|
||||
continue;
|
||||
}
|
||||
result = result || [];
|
||||
result.push({ open: pair[0], close: pair[1] });
|
||||
} else {
|
||||
if (!types.isObject(pair)) {
|
||||
console.warn(`[${languageIdentifier.language}]: language configuration: expected \`surroundingPairs[${i}]\` to be an array of two strings or an object.`);
|
||||
continue;
|
||||
}
|
||||
if (typeof pair.open !== 'string') {
|
||||
console.warn(`[${languageIdentifier.language}]: language configuration: expected \`surroundingPairs[${i}].open\` to be a string.`);
|
||||
continue;
|
||||
}
|
||||
if (typeof pair.close !== 'string') {
|
||||
console.warn(`[${languageIdentifier.language}]: language configuration: expected \`surroundingPairs[${i}].close\` to be a string.`);
|
||||
continue;
|
||||
}
|
||||
result = result || [];
|
||||
result.push({ open: pair.open, close: pair.close });
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// private _mapCharacterPairs(pairs: Array<CharacterPair | IAutoClosingPairConditional>): IAutoClosingPairConditional[] {
|
||||
// return pairs.map(pair => {
|
||||
// if (Array.isArray(pair)) {
|
||||
// return { open: pair[0], close: pair[1] };
|
||||
// }
|
||||
// return <IAutoClosingPairConditional>pair;
|
||||
// });
|
||||
// }
|
||||
|
||||
private _handleConfig(languageIdentifier: LanguageIdentifier, configuration: ILanguageConfiguration): void {
|
||||
|
||||
let richEditConfig: LanguageConfiguration = {};
|
||||
|
||||
const comments = this._extractValidCommentRule(languageIdentifier, configuration);
|
||||
if (comments) {
|
||||
richEditConfig.comments = comments;
|
||||
}
|
||||
|
||||
const brackets = this._extractValidBrackets(languageIdentifier, configuration);
|
||||
if (brackets) {
|
||||
richEditConfig.brackets = brackets;
|
||||
}
|
||||
|
||||
const autoClosingPairs = this._extractValidAutoClosingPairs(languageIdentifier, configuration);
|
||||
if (autoClosingPairs) {
|
||||
richEditConfig.autoClosingPairs = autoClosingPairs;
|
||||
}
|
||||
|
||||
const surroundingPairs = this._extractValidSurroundingPairs(languageIdentifier, configuration);
|
||||
if (surroundingPairs) {
|
||||
richEditConfig.surroundingPairs = surroundingPairs;
|
||||
}
|
||||
|
||||
const autoCloseBefore = configuration.autoCloseBefore;
|
||||
if (typeof autoCloseBefore === 'string') {
|
||||
richEditConfig.autoCloseBefore = autoCloseBefore;
|
||||
}
|
||||
|
||||
if (configuration.wordPattern) {
|
||||
try {
|
||||
let wordPattern = this._parseRegex(configuration.wordPattern);
|
||||
if (wordPattern) {
|
||||
richEditConfig.wordPattern = wordPattern;
|
||||
}
|
||||
} catch (error) {
|
||||
// Malformed regexes are ignored
|
||||
}
|
||||
}
|
||||
|
||||
if (configuration.indentationRules) {
|
||||
let indentationRules = this._mapIndentationRules(configuration.indentationRules);
|
||||
if (indentationRules) {
|
||||
richEditConfig.indentationRules = indentationRules;
|
||||
}
|
||||
}
|
||||
|
||||
if (configuration.folding) {
|
||||
let markers = configuration.folding.markers;
|
||||
|
||||
richEditConfig.folding = {
|
||||
offSide: configuration.folding.offSide,
|
||||
markers: markers ? { start: new RegExp(markers.start), end: new RegExp(markers.end) } : undefined
|
||||
};
|
||||
}
|
||||
|
||||
LanguageConfigurationRegistry.register(languageIdentifier, richEditConfig);
|
||||
}
|
||||
|
||||
private _parseRegex(value: string | IRegExp) {
|
||||
if (typeof value === 'string') {
|
||||
return new RegExp(value, '');
|
||||
} else if (typeof value === 'object') {
|
||||
return new RegExp(value.pattern, value.flags);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private _mapIndentationRules(indentationRules: IIndentationRules): IndentationRule | null {
|
||||
try {
|
||||
let increaseIndentPattern = this._parseRegex(indentationRules.increaseIndentPattern);
|
||||
let decreaseIndentPattern = this._parseRegex(indentationRules.decreaseIndentPattern);
|
||||
|
||||
if (increaseIndentPattern && decreaseIndentPattern) {
|
||||
let result: IndentationRule = {
|
||||
increaseIndentPattern: increaseIndentPattern,
|
||||
decreaseIndentPattern: decreaseIndentPattern
|
||||
};
|
||||
|
||||
if (indentationRules.indentNextLinePattern) {
|
||||
result.indentNextLinePattern = this._parseRegex(indentationRules.indentNextLinePattern);
|
||||
}
|
||||
if (indentationRules.unIndentedLinePattern) {
|
||||
result.unIndentedLinePattern = this._parseRegex(indentationRules.unIndentedLinePattern);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
} catch (error) {
|
||||
// Malformed regexes are ignored
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const schemaId = 'vscode://schemas/language-configuration';
|
||||
const schema: IJSONSchema = {
|
||||
allowComments: true,
|
||||
default: {
|
||||
comments: {
|
||||
blockComment: ['/*', '*/'],
|
||||
lineComment: '//'
|
||||
},
|
||||
brackets: [['(', ')'], ['[', ']'], ['{', '}']],
|
||||
autoClosingPairs: [['(', ')'], ['[', ']'], ['{', '}']],
|
||||
surroundingPairs: [['(', ')'], ['[', ']'], ['{', '}']]
|
||||
},
|
||||
definitions: {
|
||||
openBracket: {
|
||||
type: 'string',
|
||||
description: nls.localize('schema.openBracket', 'The opening bracket character or string sequence.')
|
||||
},
|
||||
closeBracket: {
|
||||
type: 'string',
|
||||
description: nls.localize('schema.closeBracket', 'The closing bracket character or string sequence.')
|
||||
},
|
||||
bracketPair: {
|
||||
type: 'array',
|
||||
items: [{
|
||||
$ref: '#definitions/openBracket'
|
||||
}, {
|
||||
$ref: '#definitions/closeBracket'
|
||||
}]
|
||||
}
|
||||
},
|
||||
properties: {
|
||||
comments: {
|
||||
default: {
|
||||
blockComment: ['/*', '*/'],
|
||||
lineComment: '//'
|
||||
},
|
||||
description: nls.localize('schema.comments', 'Defines the comment symbols'),
|
||||
type: 'object',
|
||||
properties: {
|
||||
blockComment: {
|
||||
type: 'array',
|
||||
description: nls.localize('schema.blockComments', 'Defines how block comments are marked.'),
|
||||
items: [{
|
||||
type: 'string',
|
||||
description: nls.localize('schema.blockComment.begin', 'The character sequence that starts a block comment.')
|
||||
}, {
|
||||
type: 'string',
|
||||
description: nls.localize('schema.blockComment.end', 'The character sequence that ends a block comment.')
|
||||
}]
|
||||
},
|
||||
lineComment: {
|
||||
type: 'string',
|
||||
description: nls.localize('schema.lineComment', 'The character sequence that starts a line comment.')
|
||||
}
|
||||
}
|
||||
},
|
||||
brackets: {
|
||||
default: [['(', ')'], ['[', ']'], ['{', '}']],
|
||||
description: nls.localize('schema.brackets', 'Defines the bracket symbols that increase or decrease the indentation.'),
|
||||
type: 'array',
|
||||
items: {
|
||||
$ref: '#definitions/bracketPair'
|
||||
}
|
||||
},
|
||||
autoClosingPairs: {
|
||||
default: [['(', ')'], ['[', ']'], ['{', '}']],
|
||||
description: nls.localize('schema.autoClosingPairs', 'Defines the bracket pairs. When a opening bracket is entered, the closing bracket is inserted automatically.'),
|
||||
type: 'array',
|
||||
items: {
|
||||
oneOf: [{
|
||||
$ref: '#definitions/bracketPair'
|
||||
}, {
|
||||
type: 'object',
|
||||
properties: {
|
||||
open: {
|
||||
$ref: '#definitions/openBracket'
|
||||
},
|
||||
close: {
|
||||
$ref: '#definitions/closeBracket'
|
||||
},
|
||||
notIn: {
|
||||
type: 'array',
|
||||
description: nls.localize('schema.autoClosingPairs.notIn', 'Defines a list of scopes where the auto pairs are disabled.'),
|
||||
items: {
|
||||
enum: ['string', 'comment']
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
autoCloseBefore: {
|
||||
default: ';:.,=}])> \n\t',
|
||||
description: nls.localize('schema.autoCloseBefore', 'Defines what characters must be after the cursor in order for bracket or quote autoclosing to occur when using the \'languageDefined\' autoclosing setting. This is typically the set of characters which can not start an expression.'),
|
||||
type: 'string',
|
||||
},
|
||||
surroundingPairs: {
|
||||
default: [['(', ')'], ['[', ']'], ['{', '}']],
|
||||
description: nls.localize('schema.surroundingPairs', 'Defines the bracket pairs that can be used to surround a selected string.'),
|
||||
type: 'array',
|
||||
items: {
|
||||
oneOf: [{
|
||||
$ref: '#definitions/bracketPair'
|
||||
}, {
|
||||
type: 'object',
|
||||
properties: {
|
||||
open: {
|
||||
$ref: '#definitions/openBracket'
|
||||
},
|
||||
close: {
|
||||
$ref: '#definitions/closeBracket'
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
wordPattern: {
|
||||
default: '',
|
||||
description: nls.localize('schema.wordPattern', 'Defines what is considered to be a word in the programming language.'),
|
||||
type: ['string', 'object'],
|
||||
properties: {
|
||||
pattern: {
|
||||
type: 'string',
|
||||
description: nls.localize('schema.wordPattern.pattern', 'The RegExp pattern used to match words.'),
|
||||
default: '',
|
||||
},
|
||||
flags: {
|
||||
type: 'string',
|
||||
description: nls.localize('schema.wordPattern.flags', 'The RegExp flags used to match words.'),
|
||||
default: 'g',
|
||||
pattern: '^([gimuy]+)$',
|
||||
patternErrorMessage: nls.localize('schema.wordPattern.flags.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.')
|
||||
}
|
||||
}
|
||||
},
|
||||
indentationRules: {
|
||||
default: {
|
||||
increaseIndentPattern: '',
|
||||
decreaseIndentPattern: ''
|
||||
},
|
||||
description: nls.localize('schema.indentationRules', 'The language\'s indentation settings.'),
|
||||
type: 'object',
|
||||
properties: {
|
||||
increaseIndentPattern: {
|
||||
type: ['string', 'object'],
|
||||
description: nls.localize('schema.indentationRules.increaseIndentPattern', 'If a line matches this pattern, then all the lines after it should be indented once (until another rule matches).'),
|
||||
properties: {
|
||||
pattern: {
|
||||
type: 'string',
|
||||
description: nls.localize('schema.indentationRules.increaseIndentPattern.pattern', 'The RegExp pattern for increaseIndentPattern.'),
|
||||
default: '',
|
||||
},
|
||||
flags: {
|
||||
type: 'string',
|
||||
description: nls.localize('schema.indentationRules.increaseIndentPattern.flags', 'The RegExp flags for increaseIndentPattern.'),
|
||||
default: '',
|
||||
pattern: '^([gimuy]+)$',
|
||||
patternErrorMessage: nls.localize('schema.indentationRules.increaseIndentPattern.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.')
|
||||
}
|
||||
}
|
||||
},
|
||||
decreaseIndentPattern: {
|
||||
type: ['string', 'object'],
|
||||
description: nls.localize('schema.indentationRules.decreaseIndentPattern', 'If a line matches this pattern, then all the lines after it should be unindented once (until another rule matches).'),
|
||||
properties: {
|
||||
pattern: {
|
||||
type: 'string',
|
||||
description: nls.localize('schema.indentationRules.decreaseIndentPattern.pattern', 'The RegExp pattern for decreaseIndentPattern.'),
|
||||
default: '',
|
||||
},
|
||||
flags: {
|
||||
type: 'string',
|
||||
description: nls.localize('schema.indentationRules.decreaseIndentPattern.flags', 'The RegExp flags for decreaseIndentPattern.'),
|
||||
default: '',
|
||||
pattern: '^([gimuy]+)$',
|
||||
patternErrorMessage: nls.localize('schema.indentationRules.decreaseIndentPattern.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.')
|
||||
}
|
||||
}
|
||||
},
|
||||
indentNextLinePattern: {
|
||||
type: ['string', 'object'],
|
||||
description: nls.localize('schema.indentationRules.indentNextLinePattern', 'If a line matches this pattern, then **only the next line** after it should be indented once.'),
|
||||
properties: {
|
||||
pattern: {
|
||||
type: 'string',
|
||||
description: nls.localize('schema.indentationRules.indentNextLinePattern.pattern', 'The RegExp pattern for indentNextLinePattern.'),
|
||||
default: '',
|
||||
},
|
||||
flags: {
|
||||
type: 'string',
|
||||
description: nls.localize('schema.indentationRules.indentNextLinePattern.flags', 'The RegExp flags for indentNextLinePattern.'),
|
||||
default: '',
|
||||
pattern: '^([gimuy]+)$',
|
||||
patternErrorMessage: nls.localize('schema.indentationRules.indentNextLinePattern.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.')
|
||||
}
|
||||
}
|
||||
},
|
||||
unIndentedLinePattern: {
|
||||
type: ['string', 'object'],
|
||||
description: nls.localize('schema.indentationRules.unIndentedLinePattern', 'If a line matches this pattern, then its indentation should not be changed and it should not be evaluated against the other rules.'),
|
||||
properties: {
|
||||
pattern: {
|
||||
type: 'string',
|
||||
description: nls.localize('schema.indentationRules.unIndentedLinePattern.pattern', 'The RegExp pattern for unIndentedLinePattern.'),
|
||||
default: '',
|
||||
},
|
||||
flags: {
|
||||
type: 'string',
|
||||
description: nls.localize('schema.indentationRules.unIndentedLinePattern.flags', 'The RegExp flags for unIndentedLinePattern.'),
|
||||
default: '',
|
||||
pattern: '^([gimuy]+)$',
|
||||
patternErrorMessage: nls.localize('schema.indentationRules.unIndentedLinePattern.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
folding: {
|
||||
type: 'object',
|
||||
description: nls.localize('schema.folding', 'The language\'s folding settings.'),
|
||||
properties: {
|
||||
offSide: {
|
||||
type: 'boolean',
|
||||
description: nls.localize('schema.folding.offSide', 'A language adheres to the off-side rule if blocks in that language are expressed by their indentation. If set, empty lines belong to the subsequent block.'),
|
||||
},
|
||||
markers: {
|
||||
type: 'object',
|
||||
description: nls.localize('schema.folding.markers', 'Language specific folding markers such as \'#region\' and \'#endregion\'. The start and end regexes will be tested against the contents of all lines and must be designed efficiently'),
|
||||
properties: {
|
||||
start: {
|
||||
type: 'string',
|
||||
description: nls.localize('schema.folding.markers.start', 'The RegExp pattern for the start marker. The regexp must start with \'^\'.')
|
||||
},
|
||||
end: {
|
||||
type: 'string',
|
||||
description: nls.localize('schema.folding.markers.end', 'The RegExp pattern for the end marker. The regexp must start with \'^\'.')
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
let schemaRegistry = Registry.as<IJSONContributionRegistry>(Extensions.JSONContribution);
|
||||
schemaRegistry.registerSchema(schemaId, schema);
|
||||
@@ -0,0 +1,84 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
|
||||
/**
|
||||
* Shows a message when opening a large file which has been memory optimized (and features disabled).
|
||||
*/
|
||||
export class LargeFileOptimizationsWarner extends Disposable implements IEditorContribution {
|
||||
|
||||
private static readonly ID = 'editor.contrib.largeFileOptimizationsWarner';
|
||||
|
||||
private _isDisabled: boolean;
|
||||
|
||||
constructor(
|
||||
private readonly _editor: ICodeEditor,
|
||||
@INotificationService private readonly _notificationService: INotificationService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@IStorageService private readonly _storageService: IStorageService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this._isDisabled = Boolean(this._storageService.getBoolean('editor.neverPromptForLargeFiles', StorageScope.GLOBAL, false));
|
||||
|
||||
this._register(this._editor.onDidChangeModel((e) => {
|
||||
const model = this._editor.getModel();
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
if (this._isDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (model.isTooLargeForTokenization()) {
|
||||
const message = nls.localize(
|
||||
{
|
||||
key: 'largeFile',
|
||||
comment: [
|
||||
'Variable 0 will be a file name.'
|
||||
]
|
||||
},
|
||||
"{0}: tokenization, wrapping and folding have been turned off for this large file in order to reduce memory usage and avoid freezing or crashing.",
|
||||
path.basename(model.uri.path)
|
||||
);
|
||||
|
||||
this._notificationService.prompt(Severity.Info, message, [
|
||||
{
|
||||
label: nls.localize('dontShowAgain', "Don't Show Again"),
|
||||
run: () => {
|
||||
this._isDisabled = true;
|
||||
this._storageService.store('editor.neverPromptForLargeFiles', true, StorageScope.GLOBAL);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: nls.localize('removeOptimizations', "Forcefully enable features"),
|
||||
run: () => {
|
||||
this._configurationService.updateValue(`editor.largeFileOptimizations`, false).then(() => {
|
||||
this._notificationService.info(nls.localize('reopenFilePrompt', "Please reopen file in order for this setting to take effect."));
|
||||
}, (err) => {
|
||||
this._notificationService.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
]);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
return LargeFileOptimizationsWarner.ID;
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorContribution(LargeFileOptimizationsWarner);
|
||||
64
src/vs/workbench/contrib/codeEditor/browser/menuPreventer.ts
Normal file
64
src/vs/workbench/contrib/codeEditor/browser/menuPreventer.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
|
||||
/**
|
||||
* Prevents the top-level menu from showing up when doing Alt + Click in the editor
|
||||
*/
|
||||
export class MenuPreventer extends Disposable implements IEditorContribution {
|
||||
|
||||
private static readonly ID = 'editor.contrib.menuPreventer';
|
||||
|
||||
private _editor: ICodeEditor;
|
||||
private _altListeningMouse: boolean;
|
||||
private _altMouseTriggered: boolean;
|
||||
|
||||
constructor(editor: ICodeEditor) {
|
||||
super();
|
||||
this._editor = editor;
|
||||
this._altListeningMouse = false;
|
||||
this._altMouseTriggered = false;
|
||||
|
||||
// A global crossover handler to prevent menu bar from showing up
|
||||
// When <alt> is hold, we will listen to mouse events and prevent
|
||||
// the release event up <alt> if the mouse is triggered.
|
||||
|
||||
this._register(this._editor.onMouseDown((e) => {
|
||||
if (this._altListeningMouse) {
|
||||
this._altMouseTriggered = true;
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(this._editor.onKeyDown((e) => {
|
||||
if (e.equals(KeyMod.Alt)) {
|
||||
if (!this._altListeningMouse) {
|
||||
this._altMouseTriggered = false;
|
||||
}
|
||||
this._altListeningMouse = true;
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(this._editor.onKeyUp((e) => {
|
||||
if (e.equals(KeyMod.Alt)) {
|
||||
if (this._altMouseTriggered) {
|
||||
e.preventDefault();
|
||||
}
|
||||
this._altListeningMouse = false;
|
||||
this._altMouseTriggered = false;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
return MenuPreventer.ID;
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorContribution(MenuPreventer);
|
||||
@@ -0,0 +1,116 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import * as process from 'vs/base/common/process';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
|
||||
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
|
||||
import { ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { EndOfLinePreference } from 'vs/editor/common/model';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
|
||||
export class SelectionClipboard extends Disposable implements IEditorContribution {
|
||||
private static SELECTION_LENGTH_LIMIT = 65536;
|
||||
private static readonly ID = 'editor.contrib.selectionClipboard';
|
||||
|
||||
constructor(editor: ICodeEditor, @IClipboardService clipboardService: IClipboardService) {
|
||||
super();
|
||||
|
||||
if (platform.isLinux) {
|
||||
let isEnabled = editor.getConfiguration().contribInfo.selectionClipboard;
|
||||
|
||||
this._register(editor.onDidChangeConfiguration((e: IConfigurationChangedEvent) => {
|
||||
if (e.contribInfo) {
|
||||
isEnabled = editor.getConfiguration().contribInfo.selectionClipboard;
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(editor.onMouseDown((e: IEditorMouseEvent) => {
|
||||
if (!isEnabled) {
|
||||
return;
|
||||
}
|
||||
if (!editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
if (e.event.middleButton) {
|
||||
e.event.preventDefault();
|
||||
editor.focus();
|
||||
|
||||
if (e.target.position) {
|
||||
editor.setPosition(e.target.position);
|
||||
}
|
||||
|
||||
if (e.target.type === MouseTargetType.SCROLLBAR) {
|
||||
return;
|
||||
}
|
||||
|
||||
process.nextTick(() => {
|
||||
// TODO@Alex: electron weirdness: calling clipboard.readText('selection') generates a paste event, so no need to execute paste ourselves
|
||||
clipboardService.readText('selection');
|
||||
// keybindingService.executeCommand(Handler.Paste, {
|
||||
// text: clipboard.readText('selection'),
|
||||
// pasteOnNewLine: false
|
||||
// });
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
let setSelectionToClipboard = this._register(new RunOnceScheduler(() => {
|
||||
if (!editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
let model = editor.getModel();
|
||||
let selections = editor.getSelections();
|
||||
selections = selections.slice(0);
|
||||
selections.sort(Range.compareRangesUsingStarts);
|
||||
|
||||
let resultLength = 0;
|
||||
for (const sel of selections) {
|
||||
if (sel.isEmpty()) {
|
||||
// Only write if all cursors have selection
|
||||
return;
|
||||
}
|
||||
resultLength += model.getValueLengthInRange(sel);
|
||||
}
|
||||
|
||||
if (resultLength > SelectionClipboard.SELECTION_LENGTH_LIMIT) {
|
||||
// This is a large selection!
|
||||
// => do not write it to the selection clipboard
|
||||
return;
|
||||
}
|
||||
|
||||
let result: string[] = [];
|
||||
for (const sel of selections) {
|
||||
result.push(model.getValueInRange(sel, EndOfLinePreference.TextDefined));
|
||||
}
|
||||
|
||||
let textToCopy = result.join(model.getEOL());
|
||||
clipboardService.writeText(textToCopy, 'selection');
|
||||
}, 100));
|
||||
|
||||
this._register(editor.onDidChangeCursorSelection((e: ICursorSelectionChangedEvent) => {
|
||||
if (!isEnabled) {
|
||||
return;
|
||||
}
|
||||
setSelectionToClipboard.schedule();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
return SelectionClipboard.ID;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorContribution(SelectionClipboard);
|
||||
@@ -0,0 +1,52 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import { ContextMenuController } from 'vs/editor/contrib/contextmenu/contextmenu';
|
||||
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
|
||||
import { SuggestController } from 'vs/editor/contrib/suggest/suggestController';
|
||||
import { MenuPreventer } from 'vs/workbench/contrib/codeEditor/browser/menuPreventer';
|
||||
import { SelectionClipboard } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard';
|
||||
import { TabCompletionController } from 'vs/workbench/contrib/snippets/browser/tabCompletion';
|
||||
|
||||
export function getSimpleEditorOptions(): IEditorOptions {
|
||||
return {
|
||||
wordWrap: 'on',
|
||||
overviewRulerLanes: 0,
|
||||
glyphMargin: false,
|
||||
lineNumbers: 'off',
|
||||
folding: false,
|
||||
selectOnLineNumbers: false,
|
||||
hideCursorInOverviewRuler: true,
|
||||
selectionHighlight: false,
|
||||
scrollbar: {
|
||||
horizontal: 'hidden'
|
||||
},
|
||||
lineDecorationsWidth: 0,
|
||||
overviewRulerBorder: false,
|
||||
scrollBeyondLastLine: false,
|
||||
renderLineHighlight: 'none',
|
||||
fixedOverflowWidgets: true,
|
||||
acceptSuggestionOnEnter: 'smart',
|
||||
minimap: {
|
||||
enabled: false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function getSimpleCodeEditorWidgetOptions(): ICodeEditorWidgetOptions {
|
||||
return {
|
||||
isSimpleWidget: true,
|
||||
contributions: [
|
||||
MenuPreventer,
|
||||
SelectionClipboard,
|
||||
ContextMenuController,
|
||||
SuggestController,
|
||||
SnippetController2,
|
||||
TabCompletionController,
|
||||
]
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.st0{opacity:0}.st0,.st1{fill:#f6f6f6}.st2{fill:#424242}</style><g id="outline"><path class="st0" d="M0 0h16v16H0z"/><path class="st1" d="M16 3H9v.445c-.034-.07-.062-.145-.1-.21a2.318 2.318 0 0 0-.759-.793C7.81 2.229 7 2.117 7 2.109V0H4v2.438c-.078-.042-.15-.092-.233-.125a2.685 2.685 0 0 0-1.013-.188c-.184 0-.392.016-.619.055a5.422 5.422 0 0 0-.506.115c-.149.045-.337.096-.493.162-.173.077-.39.151-.533.25L0 3.006v3.17c0 .297.045.56.136.821.1.291.255.525.454.736.207.224.462.351.75.467.065.027.137.026.205.039a2.827 2.827 0 0 0-.651.532 3.064 3.064 0 0 0-.6 1.061 3.81 3.81 0 0 0-.188 1.21c0 .402.062.784.185 1.137.132.377.323.709.562.978a2.748 2.748 0 0 0 2.131.942c.333 0 .595-.042.876-.13a2.61 2.61 0 0 0 .678-.333L5 13.225v-1.811L8.586 15h4.828l-2-2H16V3zM5 10.586V8.523l-.556-.404c-.081-.043-.152-.083-.228-.119h1.96c.196 0 .409.266.634.266.187 0 .366-.007.538-.029L5 10.586z"/></g><g id="icon_x5F_bg"><path class="st2" d="M10 4v2h3v4H9l2-2H9l-3 3 3 3h2l-2-2h6V4zM3.869 3.568a1.21 1.21 0 0 0-.473-.329c-.274-.111-.623-.15-1.055-.076a3.5 3.5 0 0 0-.711.208 1.501 1.501 0 0 0-.234.125l-.043.03v1.056l.168-.139c.149-.124.326-.225.527-.303.196-.074.399-.113.604-.113.188 0 .33.051.431.157.087.095.137.248.147.456l-.962.144c-.219.03-.41.086-.57.166a1.245 1.245 0 0 0-.398.311 1.234 1.234 0 0 0-.229.426 1.714 1.714 0 0 0 .011 1.008 1.096 1.096 0 0 0 .638.67c.155.063.328.093.528.093a1.25 1.25 0 0 0 .978-.441v.345h1.007V4.769c0-.255-.03-.484-.089-.681a1.423 1.423 0 0 0-.275-.52zm-.636 1.896V5.7c0 .119-.018.231-.055.341a.745.745 0 0 1-.377.447.694.694 0 0 1-.512.027.454.454 0 0 1-.156-.094.389.389 0 0 1-.094-.139.474.474 0 0 1-.035-.186c0-.077.009-.147.024-.212a.33.33 0 0 1 .078-.141.436.436 0 0 1 .161-.109 1.3 1.3 0 0 1 .305-.073l.661-.097zM8.284 4.397a2.253 2.253 0 0 0-.244-.656 1.354 1.354 0 0 0-.436-.459 1.165 1.165 0 0 0-.642-.173 1.136 1.136 0 0 0-.69.223 1.312 1.312 0 0 0-.264.266V1.119H5.09v6.224h.918v-.281a1.023 1.023 0 0 0 .472.328c.098.032.208.047.33.047.255 0 .483-.06.677-.177.192-.115.355-.278.486-.486a2.29 2.29 0 0 0 .293-.718 3.87 3.87 0 0 0 .096-.886 3.76 3.76 0 0 0-.078-.773zm-.861.758c0 .232-.02.439-.059.613-.036.172-.09.315-.159.424a.639.639 0 0 1-.233.237.582.582 0 0 1-.565.014.683.683 0 0 1-.211-.183.925.925 0 0 1-.141-.283 1.187 1.187 0 0 1-.054-.358v-.517c0-.164.02-.314.059-.447.037-.132.088-.242.157-.336a.668.668 0 0 1 .228-.208.584.584 0 0 1 .289-.071.554.554 0 0 1 .497.279c.063.099.108.214.143.354.031.143.049.306.049.482zM2.409 10.019a.913.913 0 0 1 .316-.239c.218-.1.547-.105.766-.018.104.042.204.1.32.184l.329.26V9.064l-.096-.062a1.932 1.932 0 0 0-.905-.215c-.308 0-.593.057-.846.168-.25.11-.467.27-.647.475a2.072 2.072 0 0 0-.403.717c-.09.272-.137.57-.137.895 0 .289.043.561.129.808.087.249.212.471.374.652.161.185.361.333.597.441.232.104.493.155.778.155.233 0 .434-.028.613-.084a1.85 1.85 0 0 0 .466-.217l.078-.061v-.889l-.2.095a.402.402 0 0 1-.076.026c-.05.017-.099.035-.128.049-.036.023-.227.09-.227.09-.06.024-.14.043-.218.059a.977.977 0 0 1-.599-.057.827.827 0 0 1-.306-.225 1.088 1.088 0 0 1-.205-.376 1.728 1.728 0 0 1-.076-.529c0-.21.028-.399.083-.56.054-.158.129-.294.22-.4z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
@@ -0,0 +1,28 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.suggest-input-container {
|
||||
padding: 3px 4px 5px;
|
||||
}
|
||||
|
||||
.suggest-input-container .monaco-editor-background,
|
||||
.suggest-input-container .monaco-editor,
|
||||
.suggest-input-container .mtk1 {
|
||||
/* allow the embedded monaco to be styled from the outer context */
|
||||
background-color: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.suggest-input-container .suggest-input-placeholder {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
pointer-events: none;
|
||||
margin-top: 2px;
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./suggestEnabledInput';
|
||||
import { $, Dimension, addClass, append, removeClass } from 'vs/base/browser/dom';
|
||||
import { Widget } from 'vs/base/browser/ui/widget';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { ContextMenuController } from 'vs/editor/contrib/contextmenu/contextmenu';
|
||||
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
|
||||
import { SuggestController } from 'vs/editor/contrib/suggest/suggestController';
|
||||
import { IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ColorIdentifier, editorSelectionBackground, inputBackground, inputBorder, inputForeground, inputPlaceholderForeground, selectionBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IStyleOverrides, IThemable, attachStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { MenuPreventer } from 'vs/workbench/contrib/codeEditor/browser/menuPreventer';
|
||||
import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions';
|
||||
import { SelectionClipboard } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard';
|
||||
|
||||
interface SuggestResultsProvider {
|
||||
/**
|
||||
* Provider function for suggestion results.
|
||||
*
|
||||
* @param query the full text of the input.
|
||||
*/
|
||||
provideResults: (query: string) => string[];
|
||||
|
||||
/**
|
||||
* Trigger characters for this input. Suggestions will appear when one of these is typed,
|
||||
* or upon `ctrl+space` triggering at a word boundary.
|
||||
*
|
||||
* Defaults to the empty array.
|
||||
*/
|
||||
triggerCharacters?: string[];
|
||||
|
||||
/**
|
||||
* Defines the sorting function used when showing results.
|
||||
*
|
||||
* Defaults to the identity function.
|
||||
*/
|
||||
sortKey?: (result: string) => string;
|
||||
}
|
||||
|
||||
interface SuggestEnabledInputOptions {
|
||||
/**
|
||||
* The text to show when no input is present.
|
||||
*
|
||||
* Defaults to the empty string.
|
||||
*/
|
||||
placeholderText?: string;
|
||||
value?: string;
|
||||
|
||||
/**
|
||||
* Context key tracking the focus state of this element
|
||||
*/
|
||||
focusContextKey?: IContextKey<boolean>;
|
||||
}
|
||||
|
||||
export interface ISuggestEnabledInputStyleOverrides extends IStyleOverrides {
|
||||
inputBackground?: ColorIdentifier;
|
||||
inputForeground?: ColorIdentifier;
|
||||
inputBorder?: ColorIdentifier;
|
||||
inputPlaceholderForeground?: ColorIdentifier;
|
||||
}
|
||||
|
||||
type ISuggestEnabledInputStyles = {
|
||||
[P in keyof ISuggestEnabledInputStyleOverrides]: Color;
|
||||
};
|
||||
|
||||
export function attachSuggestEnabledInputBoxStyler(widget: IThemable, themeService: IThemeService, style?: ISuggestEnabledInputStyleOverrides): IDisposable {
|
||||
return attachStyler(themeService, {
|
||||
inputBackground: (style && style.inputBackground) || inputBackground,
|
||||
inputForeground: (style && style.inputForeground) || inputForeground,
|
||||
inputBorder: (style && style.inputBorder) || inputBorder,
|
||||
inputPlaceholderForeground: (style && style.inputPlaceholderForeground) || inputPlaceholderForeground,
|
||||
} as ISuggestEnabledInputStyleOverrides, widget);
|
||||
}
|
||||
|
||||
export class SuggestEnabledInput extends Widget implements IThemable {
|
||||
|
||||
private _onShouldFocusResults = new Emitter<void>();
|
||||
readonly onShouldFocusResults: Event<void> = this._onShouldFocusResults.event;
|
||||
|
||||
private _onEnter = new Emitter<void>();
|
||||
readonly onEnter: Event<void> = this._onEnter.event;
|
||||
|
||||
private _onInputDidChange = new Emitter<string | undefined>();
|
||||
readonly onInputDidChange: Event<string | undefined> = this._onInputDidChange.event;
|
||||
|
||||
private disposables: IDisposable[] = [];
|
||||
private inputWidget: CodeEditorWidget;
|
||||
private stylingContainer: HTMLDivElement;
|
||||
private placeholderText: HTMLDivElement;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
parent: HTMLElement,
|
||||
suggestionProvider: SuggestResultsProvider,
|
||||
ariaLabel: string,
|
||||
resourceHandle: string,
|
||||
options: SuggestEnabledInputOptions,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IModelService modelService: IModelService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.stylingContainer = append(parent, $('.suggest-input-container'));
|
||||
this.placeholderText = append(this.stylingContainer, $('.suggest-input-placeholder', undefined, options.placeholderText || ''));
|
||||
|
||||
const editorOptions: IEditorOptions = mixin(
|
||||
getSimpleEditorOptions(),
|
||||
getSuggestEnabledInputOptions(ariaLabel));
|
||||
|
||||
this.inputWidget = instantiationService.createInstance(CodeEditorWidget, this.stylingContainer,
|
||||
editorOptions,
|
||||
{
|
||||
contributions: [SuggestController, SnippetController2, ContextMenuController, MenuPreventer, SelectionClipboard],
|
||||
isSimpleWidget: true,
|
||||
});
|
||||
this.disposables.push(this.inputWidget);
|
||||
|
||||
let scopeHandle = uri.parse(resourceHandle);
|
||||
this.inputWidget.setModel(modelService.createModel('', null, scopeHandle, true));
|
||||
|
||||
this.disposables.push(this.inputWidget.onDidPaste(() => this.setValue(this.getValue()))); // setter cleanses
|
||||
|
||||
this.disposables.push((this.inputWidget.onDidFocusEditorText(() => {
|
||||
if (options.focusContextKey) { options.focusContextKey.set(true); }
|
||||
addClass(this.stylingContainer, 'synthetic-focus');
|
||||
})));
|
||||
this.disposables.push((this.inputWidget.onDidBlurEditorText(() => {
|
||||
if (options.focusContextKey) { options.focusContextKey.set(false); }
|
||||
removeClass(this.stylingContainer, 'synthetic-focus');
|
||||
})));
|
||||
|
||||
const onKeyDownMonaco = Event.chain(this.inputWidget.onKeyDown);
|
||||
onKeyDownMonaco.filter(e => e.keyCode === KeyCode.Enter).on(e => { e.preventDefault(); this._onEnter.fire(); }, this, this.disposables);
|
||||
onKeyDownMonaco.filter(e => e.keyCode === KeyCode.DownArrow && (isMacintosh ? e.metaKey : e.ctrlKey)).on(() => this._onShouldFocusResults.fire(), this, this.disposables);
|
||||
|
||||
let preexistingContent = this.getValue();
|
||||
const inputWidgetModel = this.inputWidget.getModel();
|
||||
if (inputWidgetModel) {
|
||||
this.disposables.push(inputWidgetModel.onDidChangeContent(() => {
|
||||
let content = this.getValue();
|
||||
this.placeholderText.style.visibility = content ? 'hidden' : 'visible';
|
||||
if (preexistingContent.trim() === content.trim()) { return; }
|
||||
this._onInputDidChange.fire(undefined);
|
||||
preexistingContent = content;
|
||||
}));
|
||||
}
|
||||
|
||||
let validatedSuggestProvider = {
|
||||
provideResults: suggestionProvider.provideResults,
|
||||
sortKey: suggestionProvider.sortKey || (a => a),
|
||||
triggerCharacters: suggestionProvider.triggerCharacters || []
|
||||
};
|
||||
|
||||
this.setValue(options.value || '');
|
||||
|
||||
this.disposables.push(modes.CompletionProviderRegistry.register({ scheme: scopeHandle.scheme, pattern: '**/' + scopeHandle.path, hasAccessToAllModels: true }, {
|
||||
triggerCharacters: validatedSuggestProvider.triggerCharacters,
|
||||
provideCompletionItems: (model: ITextModel, position: Position, _context: modes.CompletionContext) => {
|
||||
let query = model.getValue();
|
||||
|
||||
let wordStart = query.lastIndexOf(' ', position.column - 1) + 1;
|
||||
let alreadyTypedCount = position.column - wordStart - 1;
|
||||
|
||||
// dont show suggestions if the user has typed something, but hasn't used the trigger character
|
||||
if (alreadyTypedCount > 0 && (validatedSuggestProvider.triggerCharacters).indexOf(query[wordStart]) === -1) { return { suggestions: [] }; }
|
||||
|
||||
return {
|
||||
suggestions: suggestionProvider.provideResults(query).map(result => {
|
||||
return <modes.CompletionItem>{
|
||||
label: result,
|
||||
insertText: result,
|
||||
range: Range.fromPositions(position.delta(0, -alreadyTypedCount), position),
|
||||
sortText: validatedSuggestProvider.sortKey(result),
|
||||
kind: modes.CompletionItemKind.Keyword
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public get onFocus(): Event<void> { return this.inputWidget.onDidFocusEditorText; }
|
||||
|
||||
public setValue(val: string) {
|
||||
val = val.replace(/\s/g, ' ');
|
||||
const fullRange = new Range(1, 1, 1, this.getValue().length + 1);
|
||||
this.inputWidget.executeEdits('suggestEnabledInput.setValue', [EditOperation.replace(fullRange, val)]);
|
||||
this.inputWidget.setScrollTop(0);
|
||||
this.inputWidget.setPosition(new Position(1, val.length + 1));
|
||||
}
|
||||
|
||||
public getValue(): string {
|
||||
return this.inputWidget.getValue();
|
||||
}
|
||||
|
||||
|
||||
public style(colors: ISuggestEnabledInputStyles): void {
|
||||
this.stylingContainer.style.backgroundColor = colors.inputBackground ? colors.inputBackground.toString() : null;
|
||||
this.stylingContainer.style.color = colors.inputForeground ? colors.inputForeground.toString() : null;
|
||||
this.placeholderText.style.color = colors.inputPlaceholderForeground ? colors.inputPlaceholderForeground.toString() : null;
|
||||
|
||||
this.stylingContainer.style.borderWidth = '1px';
|
||||
this.stylingContainer.style.borderStyle = 'solid';
|
||||
this.stylingContainer.style.borderColor = colors.inputBorder ?
|
||||
colors.inputBorder.toString() :
|
||||
'transparent';
|
||||
|
||||
const cursor = this.stylingContainer.getElementsByClassName('cursor')[0] as HTMLDivElement;
|
||||
if (cursor) {
|
||||
cursor.style.backgroundColor = colors.inputForeground ? colors.inputForeground.toString() : null;
|
||||
}
|
||||
}
|
||||
|
||||
public focus(selectAll?: boolean): void {
|
||||
this.inputWidget.focus();
|
||||
|
||||
if (selectAll && this.inputWidget.getValue()) {
|
||||
this.selectAll();
|
||||
}
|
||||
}
|
||||
|
||||
public layout(dimension: Dimension): void {
|
||||
this.inputWidget.layout(dimension);
|
||||
this.placeholderText.style.width = `${dimension.width}px`;
|
||||
}
|
||||
|
||||
private selectAll(): void {
|
||||
this.inputWidget.setSelection(new Range(1, 1, 1, this.getValue().length + 1));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposables = dispose(this.disposables);
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// Override styles in selections.ts
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
let selectionColor = theme.getColor(selectionBackground);
|
||||
if (selectionColor) {
|
||||
selectionColor = selectionColor.transparent(0.4);
|
||||
} else {
|
||||
selectionColor = theme.getColor(editorSelectionBackground);
|
||||
}
|
||||
|
||||
if (selectionColor) {
|
||||
collector.addRule(`.suggest-input-container .monaco-editor .focused .selected-text { background-color: ${selectionColor}; }`);
|
||||
}
|
||||
|
||||
// Override inactive selection bg
|
||||
const inputBackgroundColor = theme.getColor(inputBackground);
|
||||
if (inputBackgroundColor) {
|
||||
collector.addRule(`.suggest-input-container .monaco-editor .selected-text { background-color: ${inputBackgroundColor.transparent(0.4)}; }`);
|
||||
}
|
||||
|
||||
// Override selected fg
|
||||
const inputForegroundColor = theme.getColor(inputForeground);
|
||||
if (inputForegroundColor) {
|
||||
collector.addRule(`.suggest-input-container .monaco-editor .view-line span.inline-selected-text { color: ${inputForegroundColor}; }`);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function getSuggestEnabledInputOptions(ariaLabel?: string): IEditorOptions {
|
||||
return {
|
||||
fontSize: 13,
|
||||
lineHeight: 20,
|
||||
wordWrap: 'off',
|
||||
scrollbar: { vertical: 'hidden', },
|
||||
roundedSelection: false,
|
||||
renderIndentGuides: false,
|
||||
cursorWidth: 1,
|
||||
fontFamily: ' -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif',
|
||||
ariaLabel: ariaLabel || '',
|
||||
|
||||
snippetSuggestions: 'none',
|
||||
suggest: { filterGraceful: false, showIcons: false }
|
||||
};
|
||||
}
|
||||
45
src/vs/workbench/contrib/codeEditor/browser/toggleMinimap.ts
Normal file
45
src/vs/workbench/contrib/codeEditor/browser/toggleMinimap.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
|
||||
|
||||
export class ToggleMinimapAction extends Action {
|
||||
public static readonly ID = 'editor.action.toggleMinimap';
|
||||
public static readonly LABEL = nls.localize('toggleMinimap', "View: Toggle Minimap");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(): Promise<any> {
|
||||
const newValue = !this._configurationService.getValue<boolean>('editor.minimap.enabled');
|
||||
return this._configurationService.updateValue('editor.minimap.enabled', newValue, ConfigurationTarget.USER);
|
||||
}
|
||||
}
|
||||
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMinimapAction, ToggleMinimapAction.ID, ToggleMinimapAction.LABEL), 'View: Toggle Minimap');
|
||||
|
||||
// {{SQL CARBON EDIT}} - Disable unused menu item
|
||||
// MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
|
||||
// group: '5_editor',
|
||||
// command: {
|
||||
// id: ToggleMinimapAction.ID,
|
||||
// title: nls.localize({ key: 'miToggleMinimap', comment: ['&& denotes a mnemonic'] }, "Toggle &&Minimap"),
|
||||
// toggled: ContextKeyExpr.equals('config.editor.minimap.enabled', true)
|
||||
// },
|
||||
// order: 2
|
||||
// });
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
@@ -0,0 +1,91 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Extensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
|
||||
import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
|
||||
|
||||
export class ToggleMultiCursorModifierAction extends Action {
|
||||
|
||||
public static readonly ID = 'workbench.action.toggleMultiCursorModifier';
|
||||
public static readonly LABEL = nls.localize('toggleLocation', "Toggle Multi-Cursor Modifier");
|
||||
|
||||
private static readonly multiCursorModifierConfigurationKey = 'editor.multiCursorModifier';
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(): Promise<any> {
|
||||
const editorConf = this.configurationService.getValue<{ multiCursorModifier: 'ctrlCmd' | 'alt' }>('editor');
|
||||
const newValue: 'ctrlCmd' | 'alt' = (editorConf.multiCursorModifier === 'ctrlCmd' ? 'alt' : 'ctrlCmd');
|
||||
|
||||
return this.configurationService.updateValue(ToggleMultiCursorModifierAction.multiCursorModifierConfigurationKey, newValue, ConfigurationTarget.USER);
|
||||
}
|
||||
}
|
||||
|
||||
const multiCursorModifier = new RawContextKey<string>('multiCursorModifier', 'altKey');
|
||||
|
||||
class MultiCursorModifierContextKeyController implements IWorkbenchContribution {
|
||||
|
||||
private readonly _multiCursorModifier: IContextKey<string>;
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService
|
||||
) {
|
||||
this._multiCursorModifier = multiCursorModifier.bindTo(contextKeyService);
|
||||
configurationService.onDidChangeConfiguration((e) => {
|
||||
if (e.affectsConfiguration('editor.multiCursorModifier')) {
|
||||
this._update();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _update(): void {
|
||||
const editorConf = this.configurationService.getValue<{ multiCursorModifier: 'ctrlCmd' | 'alt' }>('editor');
|
||||
const value = (editorConf.multiCursorModifier === 'ctrlCmd' ? 'ctrlCmd' : 'altKey');
|
||||
this._multiCursorModifier.set(value);
|
||||
}
|
||||
}
|
||||
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(MultiCursorModifierContextKeyController, LifecyclePhase.Restored);
|
||||
|
||||
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMultiCursorModifierAction, ToggleMultiCursorModifierAction.ID, ToggleMultiCursorModifierAction.LABEL), 'Toggle Multi-Cursor Modifier');
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, {
|
||||
group: '3_multi',
|
||||
command: {
|
||||
id: ToggleMultiCursorModifierAction.ID,
|
||||
title: nls.localize('miMultiCursorAlt', "Switch to Alt+Click for Multi-Cursor")
|
||||
},
|
||||
when: multiCursorModifier.isEqualTo('ctrlCmd'),
|
||||
order: 1
|
||||
});
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, {
|
||||
group: '3_multi',
|
||||
command: {
|
||||
id: ToggleMultiCursorModifierAction.ID,
|
||||
title: (
|
||||
platform.isMacintosh
|
||||
? nls.localize('miMultiCursorCmd', "Switch to Cmd+Click for Multi-Cursor")
|
||||
: nls.localize('miMultiCursorCtrl', "Switch to Ctrl+Click for Multi-Cursor")
|
||||
)
|
||||
},
|
||||
when: multiCursorModifier.isEqualTo('altKey'),
|
||||
order: 1
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
|
||||
|
||||
export class ToggleRenderControlCharacterAction extends Action {
|
||||
|
||||
public static readonly ID = 'editor.action.toggleRenderControlCharacter';
|
||||
public static readonly LABEL = nls.localize('toggleRenderControlCharacters', "View: Toggle Control Characters");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(): Promise<any> {
|
||||
let newRenderControlCharacters = !this._configurationService.getValue<boolean>('editor.renderControlCharacters');
|
||||
return this._configurationService.updateValue('editor.renderControlCharacters', newRenderControlCharacters, ConfigurationTarget.USER);
|
||||
}
|
||||
}
|
||||
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleRenderControlCharacterAction, ToggleRenderControlCharacterAction.ID, ToggleRenderControlCharacterAction.LABEL), 'View: Toggle Control Characters');
|
||||
|
||||
// {{SQL CARBON EDIT}} - Disable unused menu item
|
||||
// MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
|
||||
// group: '5_editor',
|
||||
// command: {
|
||||
// id: ToggleRenderControlCharacterAction.ID,
|
||||
// title: nls.localize({ key: 'miToggleRenderControlCharacters', comment: ['&& denotes a mnemonic'] }, "Toggle &&Control Characters"),
|
||||
// toggled: ContextKeyExpr.equals('config.editor.renderControlCharacters', true)
|
||||
// },
|
||||
// order: 4
|
||||
// });
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
@@ -0,0 +1,54 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
|
||||
|
||||
export class ToggleRenderWhitespaceAction extends Action {
|
||||
|
||||
public static readonly ID = 'editor.action.toggleRenderWhitespace';
|
||||
public static readonly LABEL = nls.localize('toggleRenderWhitespace', "View: Toggle Render Whitespace");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(): Promise<any> {
|
||||
const renderWhitespace = this._configurationService.getValue<string>('editor.renderWhitespace');
|
||||
|
||||
let newRenderWhitespace: string;
|
||||
if (renderWhitespace === 'none') {
|
||||
newRenderWhitespace = 'all';
|
||||
} else {
|
||||
newRenderWhitespace = 'none';
|
||||
}
|
||||
|
||||
return this._configurationService.updateValue('editor.renderWhitespace', newRenderWhitespace, ConfigurationTarget.USER);
|
||||
}
|
||||
}
|
||||
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleRenderWhitespaceAction, ToggleRenderWhitespaceAction.ID, ToggleRenderWhitespaceAction.LABEL), 'View: Toggle Render Whitespace');
|
||||
|
||||
// {{SQL CARBON EDIT}} - Disable unused menu item
|
||||
// MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
|
||||
// group: '5_editor',
|
||||
// command: {
|
||||
// id: ToggleRenderWhitespaceAction.ID,
|
||||
// title: nls.localize({ key: 'miToggleRenderWhitespace', comment: ['&& denotes a mnemonic'] }, "Toggle &&Render Whitespace"),
|
||||
// toggled: ContextKeyExpr.notEquals('config.editor.renderWhitespace', 'none')
|
||||
// },
|
||||
// order: 3
|
||||
// });
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
318
src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts
Normal file
318
src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts
Normal file
@@ -0,0 +1,318 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { EDITOR_DEFAULTS, InternalEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
|
||||
import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
|
||||
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
const transientWordWrapState = 'transientWordWrapState';
|
||||
const isWordWrapMinifiedKey = 'isWordWrapMinified';
|
||||
const isDominatedByLongLinesKey = 'isDominatedByLongLines';
|
||||
const inDiffEditorKey = 'inDiffEditor';
|
||||
|
||||
/**
|
||||
* State written/read by the toggle word wrap action and associated with a particular model.
|
||||
*/
|
||||
interface IWordWrapTransientState {
|
||||
readonly forceWordWrap: 'on' | 'off' | 'wordWrapColumn' | 'bounded';
|
||||
readonly forceWordWrapMinified: boolean;
|
||||
}
|
||||
|
||||
interface IWordWrapState {
|
||||
readonly configuredWordWrap: 'on' | 'off' | 'wordWrapColumn' | 'bounded' | undefined;
|
||||
readonly configuredWordWrapMinified: boolean;
|
||||
readonly transientState: IWordWrapTransientState | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store (in memory) the word wrap state for a particular model.
|
||||
*/
|
||||
export function writeTransientState(model: ITextModel, state: IWordWrapTransientState | null, codeEditorService: ICodeEditorService): void {
|
||||
codeEditorService.setTransientModelProperty(model, transientWordWrapState, state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read (in memory) the word wrap state for a particular model.
|
||||
*/
|
||||
function readTransientState(model: ITextModel, codeEditorService: ICodeEditorService): IWordWrapTransientState {
|
||||
return codeEditorService.getTransientModelProperty(model, transientWordWrapState);
|
||||
}
|
||||
|
||||
function readWordWrapState(model: ITextModel, configurationService: ITextResourceConfigurationService, codeEditorService: ICodeEditorService): IWordWrapState {
|
||||
const editorConfig = configurationService.getValue(model.uri, 'editor') as { wordWrap: 'on' | 'off' | 'wordWrapColumn' | 'bounded'; wordWrapMinified: boolean };
|
||||
let _configuredWordWrap = editorConfig && (typeof editorConfig.wordWrap === 'string' || typeof editorConfig.wordWrap === 'boolean') ? editorConfig.wordWrap : undefined;
|
||||
|
||||
// Compatibility with old true or false values
|
||||
if (<any>_configuredWordWrap === true) {
|
||||
_configuredWordWrap = 'on';
|
||||
} else if (<any>_configuredWordWrap === false) {
|
||||
_configuredWordWrap = 'off';
|
||||
}
|
||||
|
||||
const _configuredWordWrapMinified = editorConfig && typeof editorConfig.wordWrapMinified === 'boolean' ? editorConfig.wordWrapMinified : undefined;
|
||||
const _transientState = readTransientState(model, codeEditorService);
|
||||
return {
|
||||
configuredWordWrap: _configuredWordWrap,
|
||||
configuredWordWrapMinified: (typeof _configuredWordWrapMinified === 'boolean' ? _configuredWordWrapMinified : EDITOR_DEFAULTS.wordWrapMinified),
|
||||
transientState: _transientState
|
||||
};
|
||||
}
|
||||
|
||||
function toggleWordWrap(editor: ICodeEditor, state: IWordWrapState): IWordWrapState {
|
||||
if (state.transientState) {
|
||||
// toggle off => go to null
|
||||
return {
|
||||
configuredWordWrap: state.configuredWordWrap,
|
||||
configuredWordWrapMinified: state.configuredWordWrapMinified,
|
||||
transientState: null
|
||||
};
|
||||
}
|
||||
|
||||
const config = editor.getConfiguration();
|
||||
let transientState: IWordWrapTransientState;
|
||||
|
||||
const actualWrappingInfo = config.wrappingInfo;
|
||||
if (actualWrappingInfo.isWordWrapMinified) {
|
||||
// => wrapping due to minified file
|
||||
transientState = {
|
||||
forceWordWrap: 'off',
|
||||
forceWordWrapMinified: false
|
||||
};
|
||||
} else if (state.configuredWordWrap !== 'off') {
|
||||
// => wrapping is configured to be on (or some variant)
|
||||
transientState = {
|
||||
forceWordWrap: 'off',
|
||||
forceWordWrapMinified: false
|
||||
};
|
||||
} else {
|
||||
// => wrapping is configured to be off
|
||||
transientState = {
|
||||
forceWordWrap: 'on',
|
||||
forceWordWrapMinified: state.configuredWordWrapMinified
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
configuredWordWrap: state.configuredWordWrap,
|
||||
configuredWordWrapMinified: state.configuredWordWrapMinified,
|
||||
transientState: transientState
|
||||
};
|
||||
}
|
||||
|
||||
const TOGGLE_WORD_WRAP_ID = 'editor.action.toggleWordWrap';
|
||||
class ToggleWordWrapAction extends EditorAction {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: TOGGLE_WORD_WRAP_ID,
|
||||
label: nls.localize('toggle.wordwrap', "View: Toggle Word Wrap"),
|
||||
alias: 'View: Toggle Word Wrap',
|
||||
precondition: null,
|
||||
kbOpts: {
|
||||
kbExpr: null,
|
||||
primary: KeyMod.Alt | KeyCode.KEY_Z,
|
||||
weight: KeybindingWeight.EditorContrib
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
|
||||
if (!editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
const editorConfiguration = editor.getConfiguration();
|
||||
if (editorConfiguration.wrappingInfo.inDiffEditor) {
|
||||
// Cannot change wrapping settings inside the diff editor
|
||||
const notificationService = accessor.get(INotificationService);
|
||||
notificationService.info(nls.localize('wordWrap.notInDiffEditor', "Cannot toggle word wrap in a diff editor."));
|
||||
return;
|
||||
}
|
||||
|
||||
const textResourceConfigurationService = accessor.get(ITextResourceConfigurationService);
|
||||
const codeEditorService = accessor.get(ICodeEditorService);
|
||||
const model = editor.getModel();
|
||||
|
||||
if (!canToggleWordWrap(model.uri)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the current state
|
||||
const currentState = readWordWrapState(model, textResourceConfigurationService, codeEditorService);
|
||||
// Compute the new state
|
||||
const newState = toggleWordWrap(editor, currentState);
|
||||
// Write the new state
|
||||
// (this will cause an event and the controller will apply the state)
|
||||
writeTransientState(model, newState.transientState, codeEditorService);
|
||||
}
|
||||
}
|
||||
|
||||
class ToggleWordWrapController extends Disposable implements IEditorContribution {
|
||||
|
||||
private static readonly _ID = 'editor.contrib.toggleWordWrapController';
|
||||
|
||||
constructor(
|
||||
private readonly editor: ICodeEditor,
|
||||
@IContextKeyService readonly contextKeyService: IContextKeyService,
|
||||
@ITextResourceConfigurationService readonly configurationService: ITextResourceConfigurationService,
|
||||
@ICodeEditorService readonly codeEditorService: ICodeEditorService
|
||||
) {
|
||||
super();
|
||||
|
||||
const configuration = this.editor.getConfiguration();
|
||||
const isWordWrapMinified = this.contextKeyService.createKey(isWordWrapMinifiedKey, this._isWordWrapMinified(configuration));
|
||||
const isDominatedByLongLines = this.contextKeyService.createKey(isDominatedByLongLinesKey, this._isDominatedByLongLines(configuration));
|
||||
const inDiffEditor = this.contextKeyService.createKey(inDiffEditorKey, this._inDiffEditor(configuration));
|
||||
let currentlyApplyingEditorConfig = false;
|
||||
|
||||
this._register(editor.onDidChangeConfiguration((e) => {
|
||||
if (!e.wrappingInfo) {
|
||||
return;
|
||||
}
|
||||
const configuration = this.editor.getConfiguration();
|
||||
isWordWrapMinified.set(this._isWordWrapMinified(configuration));
|
||||
isDominatedByLongLines.set(this._isDominatedByLongLines(configuration));
|
||||
inDiffEditor.set(this._inDiffEditor(configuration));
|
||||
if (!currentlyApplyingEditorConfig) {
|
||||
// I am not the cause of the word wrap getting changed
|
||||
ensureWordWrapSettings();
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(editor.onDidChangeModel((e) => {
|
||||
ensureWordWrapSettings();
|
||||
}));
|
||||
|
||||
this._register(codeEditorService.onDidChangeTransientModelProperty(() => {
|
||||
ensureWordWrapSettings();
|
||||
}));
|
||||
|
||||
const ensureWordWrapSettings = () => {
|
||||
// Ensure correct word wrap settings
|
||||
const newModel = this.editor.getModel();
|
||||
if (!newModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const configuration = this.editor.getConfiguration();
|
||||
if (this._inDiffEditor(configuration)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!canToggleWordWrap(newModel.uri)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Read current configured values and toggle state
|
||||
const desiredState = readWordWrapState(newModel, this.configurationService, this.codeEditorService);
|
||||
|
||||
// Apply the state
|
||||
try {
|
||||
currentlyApplyingEditorConfig = true;
|
||||
this._applyWordWrapState(desiredState);
|
||||
} finally {
|
||||
currentlyApplyingEditorConfig = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private _applyWordWrapState(state: IWordWrapState): void {
|
||||
if (state.transientState) {
|
||||
// toggle is on
|
||||
this.editor.updateOptions({
|
||||
wordWrap: state.transientState.forceWordWrap,
|
||||
wordWrapMinified: state.transientState.forceWordWrapMinified
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// toggle is off
|
||||
this.editor.updateOptions({
|
||||
wordWrap: state.configuredWordWrap,
|
||||
wordWrapMinified: state.configuredWordWrapMinified
|
||||
});
|
||||
}
|
||||
|
||||
private _isWordWrapMinified(config: InternalEditorOptions): boolean {
|
||||
return config.wrappingInfo.isWordWrapMinified;
|
||||
}
|
||||
|
||||
private _isDominatedByLongLines(config: InternalEditorOptions): boolean {
|
||||
return config.wrappingInfo.isDominatedByLongLines;
|
||||
}
|
||||
|
||||
private _inDiffEditor(config: InternalEditorOptions): boolean {
|
||||
return config.wrappingInfo.inDiffEditor;
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
return ToggleWordWrapController._ID;
|
||||
}
|
||||
}
|
||||
|
||||
function canToggleWordWrap(uri: URI): boolean {
|
||||
if (!uri) {
|
||||
return false;
|
||||
}
|
||||
return (uri.scheme !== 'output' && uri.scheme !== 'vscode');
|
||||
}
|
||||
|
||||
|
||||
registerEditorContribution(ToggleWordWrapController);
|
||||
|
||||
registerEditorAction(ToggleWordWrapAction);
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
|
||||
command: {
|
||||
id: TOGGLE_WORD_WRAP_ID,
|
||||
title: nls.localize('unwrapMinified', "Disable wrapping for this file"),
|
||||
iconLocation: { dark: URI.parse(require.toUrl('vs/workbench/contrib/codeEditor/electron-browser/media/WordWrap_16x.svg')) }
|
||||
},
|
||||
group: 'navigation',
|
||||
order: 1,
|
||||
when: ContextKeyExpr.and(
|
||||
ContextKeyExpr.not(inDiffEditorKey),
|
||||
ContextKeyExpr.has(isDominatedByLongLinesKey),
|
||||
ContextKeyExpr.has(isWordWrapMinifiedKey)
|
||||
)
|
||||
});
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
|
||||
command: {
|
||||
id: TOGGLE_WORD_WRAP_ID,
|
||||
title: nls.localize('wrapMinified', "Enable wrapping for this file"),
|
||||
iconLocation: { dark: URI.parse(require.toUrl('vs/workbench/contrib/codeEditor/electron-browser/media/WordWrap_16x.svg')) }
|
||||
},
|
||||
group: 'navigation',
|
||||
order: 1,
|
||||
when: ContextKeyExpr.and(
|
||||
ContextKeyExpr.not(inDiffEditorKey),
|
||||
ContextKeyExpr.has(isDominatedByLongLinesKey),
|
||||
ContextKeyExpr.not(isWordWrapMinifiedKey)
|
||||
)
|
||||
});
|
||||
|
||||
|
||||
// View menu
|
||||
// {{SQL CARBON EDIT}} - Disable unused menu item
|
||||
// MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
|
||||
// group: '5_editor',
|
||||
// command: {
|
||||
// id: TOGGLE_WORD_WRAP_ID,
|
||||
// title: nls.localize({ key: 'miToggleWordWrap', comment: ['&& denotes a mnemonic'] }, "Toggle &&Word Wrap")
|
||||
// },
|
||||
// order: 1
|
||||
// });
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
@@ -0,0 +1,40 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { ReferencesController } from 'vs/editor/contrib/referenceSearch/referencesController';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
export class WorkbenchReferencesController extends ReferencesController {
|
||||
|
||||
public constructor(
|
||||
editor: ICodeEditor,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@ICodeEditorService editorService: ICodeEditorService,
|
||||
@INotificationService notificationService: INotificationService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
) {
|
||||
super(
|
||||
false,
|
||||
editor,
|
||||
contextKeyService,
|
||||
editorService,
|
||||
notificationService,
|
||||
instantiationService,
|
||||
storageService,
|
||||
configurationService
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
registerEditorContribution(WorkbenchReferencesController);
|
||||
@@ -0,0 +1,6 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import './sleepResumeRepaintMinimap';
|
||||
@@ -0,0 +1,23 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
|
||||
import { ipcRenderer as ipc } from 'electron';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
|
||||
class SleepResumeRepaintMinimap implements IWorkbenchContribution {
|
||||
|
||||
constructor(
|
||||
@ICodeEditorService codeEditorService: ICodeEditorService
|
||||
) {
|
||||
ipc.on('vscode:osResume', () => {
|
||||
codeEditorService.listCodeEditors().forEach(editor => editor.render(true));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(SleepResumeRepaintMinimap, LifecyclePhase.Eventually);
|
||||
Reference in New Issue
Block a user