Merge from vscode e558dc6ea73a75bd69d7a0b485f0e7e4194c66bf (#6864)

This commit is contained in:
Anthony Dresser
2019-08-21 20:44:59 -07:00
committed by GitHub
parent d2ae0f0154
commit 985bfae8a0
107 changed files with 2260 additions and 814 deletions

View File

@@ -12,19 +12,20 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, EditorCommand, ServicesAccessor, registerEditorAction, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_FIND_WIDGET_VISIBLE, FIND_IDS, FindModelBoundToEditorModel, ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleSearchScopeKeybinding, ToggleWholeWordKeybinding } from 'vs/editor/contrib/find/findModel';
import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_FIND_WIDGET_VISIBLE, FIND_IDS, FindModelBoundToEditorModel, ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleSearchScopeKeybinding, ToggleWholeWordKeybinding, CONTEXT_REPLACE_INPUT_FOCUSED } from 'vs/editor/contrib/find/findModel';
import { FindOptionsWidget } from 'vs/editor/contrib/find/findOptionsWidget';
import { FindReplaceState, FindReplaceStateChangedEvent, INewFindReplaceState } from 'vs/editor/contrib/find/findState';
import { FindWidget, IFindController } from 'vs/editor/contrib/find/findWidget';
import { MenuId } from 'vs/platform/actions/common/actions';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { optional } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { INotificationService } from 'vs/platform/notification/common/notification';
const SEARCH_STRING_MAX_LENGTH = 524288;
@@ -75,7 +76,7 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
protected _state: FindReplaceState;
protected _updateHistoryDelayer: Delayer<void>;
private _model: FindModelBoundToEditorModel | null;
private readonly _storageService: IStorageService;
protected readonly _storageService: IStorageService;
private readonly _clipboardService: IClipboardService;
protected readonly _contextKeyService: IContextKeyService;
@@ -383,10 +384,11 @@ export class FindController extends CommonFindController implements IFindControl
@IContextKeyService _contextKeyService: IContextKeyService,
@IKeybindingService private readonly _keybindingService: IKeybindingService,
@IThemeService private readonly _themeService: IThemeService,
@IStorageService storageService: IStorageService,
@optional(IClipboardService) clipboardService: IClipboardService
@INotificationService private readonly _notificationService: INotificationService,
@IStorageService _storageService: IStorageService,
@optional(IClipboardService) clipboardService: IClipboardService,
) {
super(editor, _contextKeyService, storageService, clipboardService);
super(editor, _contextKeyService, _storageService, clipboardService);
this._widget = null;
this._findOptionsWidget = null;
}
@@ -422,7 +424,7 @@ export class FindController extends CommonFindController implements IFindControl
}
private _createFindWidget() {
this._widget = this._register(new FindWidget(this._editor, this, this._state, this._contextViewService, this._keybindingService, this._contextKeyService, this._themeService));
this._widget = this._register(new FindWidget(this._editor, this, this._state, this._contextViewService, this._keybindingService, this._contextKeyService, this._themeService, this._storageService, this._notificationService));
this._findOptionsWidget = this._register(new FindOptionsWidget(this._editor, this._state, this._keybindingService, this._themeService));
}
}
@@ -540,6 +542,27 @@ export class NextMatchFindAction extends MatchFindAction {
}
}
export class NextMatchFindAction2 extends MatchFindAction {
constructor() {
super({
id: FIND_IDS.NextMatchFindAction,
label: nls.localize('findNextMatchAction', "Find Next"),
alias: 'Find Next',
precondition: undefined,
kbOpts: {
kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, CONTEXT_FIND_INPUT_FOCUSED),
primary: KeyCode.Enter,
weight: KeybindingWeight.EditorContrib
}
});
}
protected _run(controller: CommonFindController): boolean {
return controller.moveToNextMatch();
}
}
export class PreviousMatchFindAction extends MatchFindAction {
constructor() {
@@ -562,6 +585,27 @@ export class PreviousMatchFindAction extends MatchFindAction {
}
}
export class PreviousMatchFindAction2 extends MatchFindAction {
constructor() {
super({
id: FIND_IDS.PreviousMatchFindAction,
label: nls.localize('findPreviousMatchAction', "Find Previous"),
alias: 'Find Previous',
precondition: undefined,
kbOpts: {
kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, CONTEXT_FIND_INPUT_FOCUSED),
primary: KeyMod.Shift | KeyCode.Enter,
weight: KeybindingWeight.EditorContrib
}
});
}
protected _run(controller: CommonFindController): boolean {
return controller.moveToPrevMatch();
}
}
export abstract class SelectionMatchFindAction extends EditorAction {
public run(accessor: ServicesAccessor | null, editor: ICodeEditor): void {
let controller = CommonFindController.get(editor);
@@ -695,7 +739,9 @@ registerEditorContribution(FindController);
registerEditorAction(StartFindAction);
registerEditorAction(StartFindWithSelectionAction);
registerEditorAction(NextMatchFindAction);
registerEditorAction(NextMatchFindAction2);
registerEditorAction(PreviousMatchFindAction);
registerEditorAction(PreviousMatchFindAction2);
registerEditorAction(NextSelectionMatchFindAction);
registerEditorAction(PreviousSelectionMatchFindAction);
registerEditorAction(StartFindReplaceAction);
@@ -781,6 +827,17 @@ registerEditorCommand(new FindCommand({
}
}));
registerEditorCommand(new FindCommand({
id: FIND_IDS.ReplaceOneAction,
precondition: CONTEXT_FIND_WIDGET_VISIBLE,
handler: x => x.replace(),
kbOpts: {
weight: KeybindingWeight.EditorContrib + 5,
kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, CONTEXT_REPLACE_INPUT_FOCUSED),
primary: KeyCode.Enter
}
}));
registerEditorCommand(new FindCommand({
id: FIND_IDS.ReplaceAllAction,
precondition: CONTEXT_FIND_WIDGET_VISIBLE,
@@ -792,6 +849,20 @@ registerEditorCommand(new FindCommand({
}
}));
registerEditorCommand(new FindCommand({
id: FIND_IDS.ReplaceAllAction,
precondition: CONTEXT_FIND_WIDGET_VISIBLE,
handler: x => x.replaceAll(),
kbOpts: {
weight: KeybindingWeight.EditorContrib + 5,
kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, CONTEXT_REPLACE_INPUT_FOCUSED),
primary: undefined,
mac: {
primary: KeyMod.CtrlCmd | KeyCode.Enter,
}
}
}));
registerEditorCommand(new FindCommand({
id: FIND_IDS.SelectAllMatchesAction,
precondition: CONTEXT_FIND_WIDGET_VISIBLE,

View File

@@ -32,8 +32,8 @@
.monaco-editor .find-widget {
position: absolute;
z-index: 10;
top: -44px; /* find input height + shadow (10px) */
height: 34px; /* find input height */
top: -44px;
height: 33px;
overflow: hidden;
line-height: 19px;
transition: top 200ms linear;
@@ -47,12 +47,10 @@
/* Find widget when replace is toggled on */
.monaco-editor .find-widget.replaceToggled {
top: -74px; /* find input height + replace input height + shadow (10px) */
height: 64px; /* find input height + replace input height */
}
.monaco-editor .find-widget.replaceToggled > .replace-part {
display: flex;
display: -webkit-flex;
align-items: center;
}
.monaco-editor .find-widget.visible,
@@ -60,13 +58,31 @@
top: 0;
}
/* Multiple line find widget */
.monaco-editor .find-widget.multipleline {
top: unset;
bottom: 10px;
}
.monaco-editor .find-widget.multipleline.visible,
.monaco-editor .find-widget.multipleline.replaceToggled.visible {
top: 0px;
bottom: unset;
}
.monaco-editor .find-widget .monaco-inputbox.synthetic-focus {
outline: 1px solid -webkit-focus-ring-color;
outline-offset: -1px;
}
.monaco-editor .find-widget .monaco-inputbox .input {
background-color: transparent;
/* Style to compensate for //winjs */
min-height: 0;
}
.monaco-editor .find-widget .replace-input .input {
.monaco-editor .find-widget .monaco-findInput .input {
font-size: 13px;
}
@@ -76,29 +92,38 @@
font-size: 12px;
display: flex;
display: -webkit-flex;
align-items: center;
}
.monaco-editor .find-widget > .find-part .monaco-inputbox,
.monaco-editor .find-widget > .replace-part .monaco-inputbox {
height: 25px;
min-height: 25px;
}
.monaco-editor .find-widget > .find-part .monaco-inputbox > .wrapper > .input {
width: 100% !important;
padding-right: 66px;
}
.monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .input {
.monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .mirror {
padding-right: 22px;
}
.monaco-editor .find-widget > .find-part .monaco-inputbox > .wrapper > .input,
.monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .input {
.monaco-editor .find-widget > .find-part .monaco-inputbox > .wrapper > .mirror,
.monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .input,
.monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .mirror {
padding-top: 2px;
padding-bottom: 2px;
}
.monaco-editor .find-widget > .find-part .find-actions {
height: 25px;
display: flex;
align-items: center;
}
.monaco-editor .find-widget > .replace-part .replace-actions {
height: 25px;
display: flex;
align-items: center;
}
.monaco-editor .find-widget .monaco-findInput {
vertical-align: middle;
display: flex;
@@ -106,6 +131,16 @@
flex:1;
}
.monaco-editor .find-widget .monaco-findInput .monaco-scrollable-element {
/* Make sure textarea inherits the width correctly */
width: 100%;
}
.monaco-editor .find-widget .monaco-findInput .monaco-scrollable-element .scrollbar.vertical {
/* Hide vertical scrollbar */
opacity: 0;
}
.monaco-editor .find-widget .matchesCount {
display: flex;
display: -webkit-flex;
@@ -237,15 +272,17 @@
display: none;
}
.monaco-editor .find-widget > .replace-part > .replace-input {
.monaco-editor .find-widget > .replace-part > .monaco-findInput {
position: relative;
display: flex;
display: -webkit-flex;
vertical-align: middle;
width: auto !important;
flex: auto;
flex-grow: 0;
flex-shrink: 0;
}
.monaco-editor .find-widget > .replace-part > .replace-input > .controls {
.monaco-editor .find-widget > .replace-part > .monaco-findInput > .controls {
position: absolute;
top: 3px;
right: 2px;

View File

@@ -9,11 +9,12 @@ import * as dom from 'vs/base/browser/dom';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { alert as alertFn } from 'vs/base/browser/ui/aria/aria';
import { FindInput, IFindInputStyles } from 'vs/base/browser/ui/findinput/findInput';
import { HistoryInputBox, IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox';
import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox';
import { ReplaceInput } from 'vs/base/browser/ui/findinput/replaceInput';
import { IHorizontalSashLayoutProvider, ISashEvent, Orientation, Sash } from 'vs/base/browser/ui/sash/sash';
import { Widget } from 'vs/base/browser/ui/widget';
import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox';
import { Delayer } from 'vs/base/common/async';
import { Color } from 'vs/base/common/color';
import { onUnexpectedError } from 'vs/base/common/errors';
@@ -28,11 +29,12 @@ import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_REPLACE_INPUT_FOCUSED, FIND_IDS, MA
import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/findState';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { contrastBorder, editorFindMatch, editorFindMatchBorder, editorFindMatchHighlight, editorFindMatchHighlightBorder, editorFindRangeHighlight, editorFindRangeHighlightBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetResizeBorder, errorForeground, inputActiveOptionBorder, inputActiveOptionBackground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry';
import { contrastBorder, editorFindMatch, editorFindMatchBorder, editorFindMatchHighlight, editorFindMatchHighlightBorder, editorFindRangeHighlight, editorFindRangeHighlightBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetResizeBorder, errorForeground, inputActiveOptionBorder, inputActiveOptionBackground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground, focusBorder } from 'vs/platform/theme/common/colorRegistry';
import { ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { ContextScopedFindInput, ContextScopedHistoryInputBox } from 'vs/platform/browser/contextScopedHistoryWidget';
import { ContextScopedFindInput, ContextScopedReplaceInput } from 'vs/platform/browser/contextScopedHistoryWidget';
import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
import { alert as alertFn } from 'vs/base/browser/ui/aria/aria';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { INotificationService } from 'vs/platform/notification/common/notification';
export interface IFindController {
replace(): void;
@@ -48,7 +50,6 @@ const NLS_TOGGLE_SELECTION_FIND_TITLE = nls.localize('label.toggleSelectionFind'
const NLS_CLOSE_BTN_LABEL = nls.localize('label.closeButton', "Close");
const NLS_REPLACE_INPUT_LABEL = nls.localize('label.replace', "Replace");
const NLS_REPLACE_INPUT_PLACEHOLDER = nls.localize('placeholder.replace', "Replace");
const NLS_PRESERVE_CASE_LABEL = nls.localize('label.preserveCaseCheckbox', "Preserve Case");
const NLS_REPLACE_BTN_LABEL = nls.localize('label.replaceButton', "Replace");
const NLS_REPLACE_ALL_BTN_LABEL = nls.localize('label.replaceAllButton', "Replace All");
const NLS_TOGGLE_REPLACE_MODE_BTN_LABEL = nls.localize('label.toggleReplaceButton', "Toggle Replace mode");
@@ -59,14 +60,12 @@ const NLS_NO_RESULTS = nls.localize('label.noResults', "No Results");
const FIND_WIDGET_INITIAL_WIDTH = 411;
const PART_WIDTH = 275;
const FIND_INPUT_AREA_WIDTH = PART_WIDTH - 54;
const REPLACE_INPUT_AREA_WIDTH = FIND_INPUT_AREA_WIDTH;
let MAX_MATCHES_COUNT_WIDTH = 69;
let FIND_ALL_CONTROLS_WIDTH = 17/** Find Input margin-left */ + (MAX_MATCHES_COUNT_WIDTH + 3 + 1) /** Match Results */ + 23 /** Button */ * 4 + 2/** sash */;
const FIND_INPUT_AREA_HEIGHT = 34; // The height of Find Widget when Replace Input is not visible.
const FIND_REPLACE_AREA_HEIGHT = 64; // The height of Find Widget when Replace Input is visible.
const FIND_INPUT_AREA_HEIGHT = 33; // The height of Find Widget when Replace Input is not visible.
const ctrlEnterReplaceAllWarningPromptedKey = 'ctrlEnterReplaceAll.windows.donotask';
export class FindWidgetViewZone implements IViewZone {
public readonly afterLineNumber: number;
@@ -84,6 +83,22 @@ export class FindWidgetViewZone implements IViewZone {
}
}
function stopPropagationForMultiLineUpwards(event: IKeyboardEvent, value: string, textarea: HTMLTextAreaElement | null) {
const isMultiline = !!value.match(/\n/);
if (textarea && isMultiline && textarea.selectionStart > 0) {
event.stopPropagation();
return;
}
}
function stopPropagationForMultiLineDownwards(event: IKeyboardEvent, value: string, textarea: HTMLTextAreaElement | null) {
const isMultiline = !!value.match(/\n/);
if (textarea && isMultiline && textarea.selectionEnd < textarea.value.length) {
event.stopPropagation();
return;
}
}
export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSashLayoutProvider {
private static readonly ID = 'editor.contrib.findWidget';
private readonly _codeEditor: ICodeEditor;
@@ -92,10 +107,13 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
private readonly _contextViewProvider: IContextViewProvider;
private readonly _keybindingService: IKeybindingService;
private readonly _contextKeyService: IContextKeyService;
private readonly _storageService: IStorageService;
private readonly _notificationService: INotificationService;
private _domNode!: HTMLElement;
private _cachedHeight: number | null;
private _findInput!: FindInput;
private _replaceInputBox!: HistoryInputBox;
private _replaceInput!: ReplaceInput;
private _toggleReplaceBtn!: SimpleButton;
private _matchesCount!: HTMLElement;
@@ -103,13 +121,13 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
private _nextBtn!: SimpleButton;
private _toggleSelectionFind!: SimpleCheckbox;
private _closeBtn!: SimpleButton;
private _preserveCase!: Checkbox;
private _replaceBtn!: SimpleButton;
private _replaceAllBtn!: SimpleButton;
private _isVisible: boolean;
private _isReplaceVisible: boolean;
private _ignoreChangeEvent: boolean;
private _ctrlEnterReplaceAllWarningPrompted: boolean;
private readonly _findFocusTracker: dom.IFocusTracker;
private readonly _findInputFocused: IContextKey<boolean>;
@@ -129,7 +147,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
contextViewProvider: IContextViewProvider,
keybindingService: IKeybindingService,
contextKeyService: IContextKeyService,
themeService: IThemeService
themeService: IThemeService,
storageService: IStorageService,
notificationService: INotificationService,
) {
super();
this._codeEditor = codeEditor;
@@ -138,6 +158,10 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._contextViewProvider = contextViewProvider;
this._keybindingService = keybindingService;
this._contextKeyService = contextKeyService;
this._storageService = storageService;
this._notificationService = notificationService;
this._ctrlEnterReplaceAllWarningPrompted = !!storageService.getBoolean(ctrlEnterReplaceAllWarningPromptedKey, StorageScope.GLOBAL);
this._isVisible = false;
this._isReplaceVisible = false;
@@ -149,6 +173,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._buildDomNode();
this._updateButtons();
this._tryUpdateWidgetWidth();
this._findInput.inputBox.layout();
this._register(this._codeEditor.onDidChangeConfiguration((e: IConfigurationChangedEvent) => {
if (e.readOnly) {
@@ -203,7 +228,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}));
this._replaceInputFocused = CONTEXT_REPLACE_INPUT_FOCUSED.bindTo(contextKeyService);
this._replaceFocusTracker = this._register(dom.trackFocus(this._replaceInputBox.inputElement));
this._replaceFocusTracker = this._register(dom.trackFocus(this._replaceInput.inputBox.inputElement));
this._register(this._replaceFocusTracker.onDidFocus(() => {
this._replaceInputFocused.set(true);
this._updateSearchScope();
@@ -264,6 +289,12 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
private _onStateChanged(e: FindReplaceStateChangedEvent): void {
if (e.searchString) {
if (this._state.searchString.indexOf('\n') >= 0) {
dom.addClass(this._domNode, 'multipleline');
} else {
dom.removeClass(this._domNode, 'multipleline');
}
try {
this._ignoreChangeEvent = true;
this._findInput.setValue(this._state.searchString);
@@ -273,7 +304,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._updateButtons();
}
if (e.replaceString) {
this._replaceInputBox.value = this._state.replaceString;
this._replaceInput.inputBox.value = this._state.replaceString;
}
if (e.isRevealed) {
if (this._state.isRevealed) {
@@ -286,8 +317,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
if (this._state.isReplaceRevealed) {
if (!this._codeEditor.getConfiguration().readOnly && !this._isReplaceVisible) {
this._isReplaceVisible = true;
this._replaceInputBox.width = this._findInput.inputBox.width;
this._replaceInput.width = dom.getTotalWidth(this._findInput.domNode);
this._updateButtons();
this._replaceInput.inputBox.layout();
}
} else {
if (this._isReplaceVisible) {
@@ -296,6 +328,12 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
}
}
if ((e.isRevealed || e.isReplaceRevealed) && (this._state.isRevealed || this._state.isReplaceRevealed)) {
if (this._tryUpdateHeight()) {
this._showViewZone();
}
}
if (e.isRegex) {
this._findInput.setRegex(this._state.isRegex);
}
@@ -337,7 +375,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._findInput.inputBox.addToHistory();
}
if (this._state.replaceString) {
this._replaceInputBox.addToHistory();
this._replaceInput.inputBox.addToHistory();
}
}
@@ -402,7 +440,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
private _updateButtons(): void {
this._findInput.setEnabled(this._isVisible);
this._replaceInputBox.setEnabled(this._isVisible && this._isReplaceVisible);
this._replaceInput.setEnabled(this._isVisible && this._isReplaceVisible);
this._updateToggleSelectionFindButton();
this._closeBtn.setEnabled(this._isVisible);
@@ -512,12 +550,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
this._codeEditor.changeViewZones((accessor) => {
if (this._state.isReplaceRevealed) {
viewZone.heightInPx = FIND_REPLACE_AREA_HEIGHT;
} else {
viewZone.heightInPx = FIND_INPUT_AREA_HEIGHT;
}
viewZone.heightInPx = this._getHeight();
this._viewZoneId = accessor.addZone(viewZone);
// scroll top adjust to make sure the editor doesn't scroll when adding viewzone at the beginning.
this._codeEditor.setScrollTop(this._codeEditor.getScrollTop() + viewZone.heightInPx);
@@ -525,30 +558,47 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
private _showViewZone(adjustScroll: boolean = true) {
const viewZone = this._viewZone;
if (!this._isVisible || !viewZone) {
if (!this._isVisible) {
return;
}
const addExtraSpaceOnTop = this._codeEditor.getConfiguration().contribInfo.find.addExtraSpaceOnTop;
if (!addExtraSpaceOnTop) {
return;
}
if (this._viewZone === undefined) {
this._viewZone = new FindWidgetViewZone(0);
}
const viewZone = this._viewZone;
this._codeEditor.changeViewZones((accessor) => {
let scrollAdjustment = FIND_INPUT_AREA_HEIGHT;
if (this._viewZoneId !== undefined) {
if (this._state.isReplaceRevealed) {
viewZone.heightInPx = FIND_REPLACE_AREA_HEIGHT;
scrollAdjustment = FIND_REPLACE_AREA_HEIGHT - FIND_INPUT_AREA_HEIGHT;
} else {
viewZone.heightInPx = FIND_INPUT_AREA_HEIGHT;
scrollAdjustment = FIND_INPUT_AREA_HEIGHT - FIND_REPLACE_AREA_HEIGHT;
// the view zone already exists, we need to update the height
const newHeight = this._getHeight();
if (newHeight === viewZone.heightInPx) {
return;
}
accessor.removeZone(this._viewZoneId);
} else {
viewZone.heightInPx = FIND_INPUT_AREA_HEIGHT;
}
this._viewZoneId = accessor.addZone(viewZone);
if (adjustScroll) {
this._codeEditor.setScrollTop(this._codeEditor.getScrollTop() + scrollAdjustment);
let scrollAdjustment = newHeight - viewZone.heightInPx;
viewZone.heightInPx = newHeight;
accessor.layoutZone(this._viewZoneId);
if (adjustScroll) {
this._codeEditor.setScrollTop(this._codeEditor.getScrollTop() + scrollAdjustment);
}
return;
} else {
const scrollAdjustment = this._getHeight();
viewZone.heightInPx = scrollAdjustment;
this._viewZoneId = accessor.addZone(viewZone);
if (adjustScroll) {
this._codeEditor.setScrollTop(this._codeEditor.getScrollTop() + scrollAdjustment);
}
}
});
}
@@ -584,8 +634,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder),
};
this._findInput.style(inputStyles);
this._replaceInputBox.style(inputStyles);
this._preserveCase.style(inputStyles);
this._replaceInput.style(inputStyles);
}
private _tryUpdateWidgetWidth() {
@@ -615,7 +664,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
if (widgetWidth > FIND_WIDGET_INITIAL_WIDTH) {
// as the widget is resized by users, we may need to change the max width of the widget as the editor width changes.
this._domNode.style.maxWidth = `${editorWidth - 28 - minimapWidth - 15}px`;
this._replaceInputBox.inputElement.style.width = `${dom.getTotalWidth(this._findInput.inputBox.inputElement)}px`;
this._replaceInput.width = dom.getTotalWidth(this._findInput.domNode);
return;
}
}
@@ -639,13 +688,47 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
if (this._resized) {
let findInputWidth = dom.getTotalWidth(this._findInput.inputBox.inputElement);
this._findInput.inputBox.layout();
let findInputWidth = this._findInput.inputBox.width;
if (findInputWidth > 0) {
this._replaceInputBox.inputElement.style.width = `${findInputWidth}px`;
this._replaceInput.width = findInputWidth;
}
}
}
private _getHeight(): number {
let totalheight = 0;
// find input margin top
totalheight += 4;
// find input height
totalheight += this._findInput.inputBox.height + 2 /** input box border */;
if (this._isReplaceVisible) {
// replace input margin
totalheight += 4;
totalheight += this._replaceInput.inputBox.height + 2 /** input box border */;
}
// margin bottom
totalheight += 4;
return totalheight;
}
private _tryUpdateHeight(): boolean {
const totalHeight = this._getHeight();
if (this._cachedHeight !== null && this._cachedHeight === totalHeight) {
return false;
}
this._cachedHeight = totalHeight;
this._domNode.style.height = `${totalHeight}px`;
return true;
}
// ----- Public
public focusFindInput(): void {
@@ -655,9 +738,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
public focusReplaceInput(): void {
this._replaceInputBox.select();
this._replaceInput.select();
// Edge browser requires focus() in addition to select()
this._replaceInputBox.focus();
this._replaceInput.focus();
}
public highlightFindOptions(): void {
@@ -692,22 +775,25 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
private _onFindInputKeyDown(e: IKeyboardEvent): void {
if (e.equals(KeyMod.WinCtrl | KeyCode.Enter)) {
const inputElement = this._findInput.inputBox.inputElement;
const start = inputElement.selectionStart;
const end = inputElement.selectionEnd;
const content = inputElement.value;
if (e.equals(KeyCode.Enter)) {
this._codeEditor.getAction(FIND_IDS.NextMatchFindAction).run().then(undefined, onUnexpectedError);
e.preventDefault();
return;
}
if (e.equals(KeyMod.Shift | KeyCode.Enter)) {
this._codeEditor.getAction(FIND_IDS.PreviousMatchFindAction).run().then(undefined, onUnexpectedError);
e.preventDefault();
return;
if (start && end) {
const value = content.substr(0, start) + '\n' + content.substr(end);
this._findInput.inputBox.value = value;
inputElement.setSelectionRange(start + 1, start + 1);
this._findInput.inputBox.layout();
e.preventDefault();
return;
}
}
if (e.equals(KeyCode.Tab)) {
if (this._isReplaceVisible) {
this._replaceInputBox.focus();
this._replaceInput.focus();
} else {
this._findInput.focusOnCaseSensitive();
}
@@ -720,20 +806,43 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
e.preventDefault();
return;
}
if (e.equals(KeyCode.UpArrow)) {
return stopPropagationForMultiLineUpwards(e, this._findInput.getValue(), this._findInput.domNode.querySelector('textarea'));
}
if (e.equals(KeyCode.DownArrow)) {
return stopPropagationForMultiLineDownwards(e, this._findInput.getValue(), this._findInput.domNode.querySelector('textarea'));
}
}
private _onReplaceInputKeyDown(e: IKeyboardEvent): void {
if (e.equals(KeyMod.WinCtrl | KeyCode.Enter)) {
if (platform.isWindows && platform.isNative && !this._ctrlEnterReplaceAllWarningPrompted) {
// this is the first time when users press Ctrl + Enter to replace all
this._notificationService.info(
nls.localize('ctrlEnter.keybindingChanged',
'Ctrl+Enter now inserts line break instead of replacing all. You can modify the keybinding for editor.action.replaceAll to override this behavior.')
);
if (e.equals(KeyCode.Enter)) {
this._controller.replace();
e.preventDefault();
return;
}
this._ctrlEnterReplaceAllWarningPrompted = true;
this._storageService.store(ctrlEnterReplaceAllWarningPromptedKey, true, StorageScope.GLOBAL);
if (e.equals(KeyMod.CtrlCmd | KeyCode.Enter)) {
this._controller.replaceAll();
e.preventDefault();
return;
}
const inputElement = this._replaceInput.inputBox.inputElement;
const start = inputElement.selectionStart;
const end = inputElement.selectionEnd;
const content = inputElement.value;
if (start && end) {
const value = content.substr(0, start) + '\n' + content.substr(end);
this._replaceInput.inputBox.value = value;
inputElement.setSelectionRange(start + 1, start + 1);
this._replaceInput.inputBox.layout();
e.preventDefault();
return;
}
}
if (e.equals(KeyCode.Tab)) {
@@ -753,6 +862,14 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
e.preventDefault();
return;
}
if (e.equals(KeyCode.UpArrow)) {
return stopPropagationForMultiLineUpwards(e, this._replaceInput.inputBox.value, this._replaceInput.inputBox.element.querySelector('textarea'));
}
if (e.equals(KeyCode.DownArrow)) {
return stopPropagationForMultiLineDownwards(e, this._replaceInput.inputBox.value, this._replaceInput.inputBox.element.querySelector('textarea'));
}
}
// ----- sash
@@ -777,6 +894,8 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
private _buildDomNode(): void {
const flexibleHeight = true;
const flexibleWidth = true;
// Find input
this._findInput = this._register(new ContextScopedFindInput(null, this._contextViewProvider, {
width: FIND_INPUT_AREA_WIDTH,
@@ -796,7 +915,10 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
} catch (e) {
return { content: e.message };
}
}
},
flexibleHeight,
flexibleWidth,
flexibleMaxHeight: 118
}, this._contextKeyService, true));
this._findInput.setRegex(!!this._state.isRegex);
this._findInput.setCaseSensitive(!!this._state.matchCase);
@@ -818,11 +940,24 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._register(this._findInput.onCaseSensitiveKeyDown((e) => {
if (e.equals(KeyMod.Shift | KeyCode.Tab)) {
if (this._isReplaceVisible) {
this._replaceInputBox.focus();
this._replaceInput.focus();
e.preventDefault();
}
}
}));
this._register(this._findInput.onRegexKeyDown((e) => {
if (e.equals(KeyCode.Tab)) {
if (this._isReplaceVisible) {
this._replaceInput.focusOnPreserve();
e.preventDefault();
}
}
}));
this._register(this._findInput.inputBox.onDidHeightChange((e) => {
if (this._tryUpdateHeight()) {
this._showViewZone();
}
}));
if (platform.isLinux) {
this._register(this._findInput.onMouseDown((e) => this._onFindInputMouseDown(e)));
}
@@ -852,13 +987,16 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
let findPart = document.createElement('div');
findPart.className = 'find-part';
findPart.appendChild(this._findInput.domNode);
findPart.appendChild(this._matchesCount);
findPart.appendChild(this._prevBtn.domNode);
findPart.appendChild(this._nextBtn.domNode);
const actionsContainer = document.createElement('div');
actionsContainer.className = 'find-actions';
findPart.appendChild(actionsContainer);
actionsContainer.appendChild(this._matchesCount);
actionsContainer.appendChild(this._prevBtn.domNode);
actionsContainer.appendChild(this._nextBtn.domNode);
// Toggle selection button
this._toggleSelectionFind = this._register(new SimpleCheckbox({
parent: findPart,
parent: actionsContainer,
title: NLS_TOGGLE_SELECTION_FIND_TITLE + this._keybindingLabelFor(FIND_IDS.ToggleSearchScopeCommand),
onChange: () => {
if (this._toggleSelectionFind.checked) {
@@ -898,34 +1036,45 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
}));
findPart.appendChild(this._closeBtn.domNode);
actionsContainer.appendChild(this._closeBtn.domNode);
// Replace input
let replaceInput = document.createElement('div');
replaceInput.className = 'replace-input';
replaceInput.style.width = REPLACE_INPUT_AREA_WIDTH + 'px';
this._replaceInputBox = this._register(new ContextScopedHistoryInputBox(replaceInput, undefined, {
ariaLabel: NLS_REPLACE_INPUT_LABEL,
this._replaceInput = this._register(new ContextScopedReplaceInput(null, undefined, {
label: NLS_REPLACE_INPUT_LABEL,
placeholder: NLS_REPLACE_INPUT_PLACEHOLDER,
history: []
}, this._contextKeyService));
this._register(dom.addStandardDisposableListener(this._replaceInputBox.inputElement, 'keydown', (e) => this._onReplaceInputKeyDown(e)));
this._register(this._replaceInputBox.onDidChange(() => {
this._state.change({ replaceString: this._replaceInputBox.value }, false);
history: [],
flexibleHeight,
flexibleWidth,
flexibleMaxHeight: 118
}, this._contextKeyService, true));
this._replaceInput.setPreserveCase(!!this._state.preserveCase);
this._register(this._replaceInput.onKeyDown((e) => this._onReplaceInputKeyDown(e)));
this._register(this._replaceInput.inputBox.onDidChange(() => {
this._state.change({ replaceString: this._replaceInput.inputBox.value }, false);
}));
this._preserveCase = this._register(new Checkbox({
actionClassName: 'monaco-preserve-case',
title: NLS_PRESERVE_CASE_LABEL,
isChecked: false,
this._register(this._replaceInput.inputBox.onDidHeightChange((e) => {
if (this._isReplaceVisible && this._tryUpdateHeight()) {
this._showViewZone();
}
}));
this._preserveCase.checked = !!this._state.preserveCase;
this._register(this._preserveCase.onChange(viaKeyboard => {
if (!viaKeyboard) {
this._state.change({ preserveCase: !this._state.preserveCase }, false);
this._replaceInputBox.focus();
this._register(this._replaceInput.onDidOptionChange(() => {
this._state.change({
preserveCase: this._replaceInput.getPreserveCase()
}, true);
}));
this._register(this._replaceInput.onPreserveCaseKeyDown((e) => {
if (e.equals(KeyCode.Tab)) {
if (this._prevBtn.isEnabled()) {
this._prevBtn.focus();
} else if (this._nextBtn.isEnabled()) {
this._nextBtn.focus();
} else if (this._toggleSelectionFind.isEnabled()) {
this._toggleSelectionFind.focus();
} else if (this._closeBtn.isEnabled()) {
this._closeBtn.focus();
}
e.preventDefault();
}
}));
@@ -953,17 +1102,16 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
}));
let controls = document.createElement('div');
controls.className = 'controls';
controls.style.display = 'block';
controls.appendChild(this._preserveCase.domNode);
replaceInput.appendChild(controls);
let replacePart = document.createElement('div');
replacePart.className = 'replace-part';
replacePart.appendChild(replaceInput);
replacePart.appendChild(this._replaceBtn.domNode);
replacePart.appendChild(this._replaceAllBtn.domNode);
replacePart.appendChild(this._replaceInput.domNode);
const replaceActionsContainer = document.createElement('div');
replaceActionsContainer.className = 'replace-actions';
replacePart.appendChild(replaceActionsContainer);
replaceActionsContainer.appendChild(this._replaceBtn.domNode);
replaceActionsContainer.appendChild(this._replaceAllBtn.domNode);
// Toggle replace button
this._toggleReplaceBtn = this._register(new SimpleButton({
@@ -972,7 +1120,8 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
onTrigger: () => {
this._state.change({ isReplaceRevealed: !this._isReplaceVisible }, false);
if (this._isReplaceVisible) {
this._replaceInputBox.width = this._findInput.inputBox.width;
this._replaceInput.width = dom.getTotalWidth(this._findInput.domNode);
this._replaceInput.inputBox.layout();
}
this._showViewZone();
}
@@ -1015,9 +1164,13 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
return;
}
this._domNode.style.width = `${width}px`;
this._findInput.inputBox.width = inputBoxWidth;
if (this._isReplaceVisible) {
this._replaceInputBox.width = inputBoxWidth;
this._replaceInput.width = dom.getTotalWidth(this._findInput.domNode);
}
this._findInput.inputBox.layout();
this._tryUpdateHeight();
}));
}
@@ -1077,6 +1230,10 @@ class SimpleCheckbox extends Widget {
return this._domNode;
}
public isEnabled(): boolean {
return (this._domNode.tabIndex >= 0);
}
public get checked(): boolean {
return this._checkbox.checked;
}
@@ -1086,7 +1243,7 @@ class SimpleCheckbox extends Widget {
}
public focus(): void {
this._checkbox.focus();
this._domNode.focus();
}
private enable(): void {
@@ -1245,4 +1402,11 @@ registerThemingParticipant((theme, collector) => {
if (inputActiveBackground) {
collector.addRule(`.monaco-editor .find-widget .monaco-checkbox .checkbox:checked + .label { background-color: ${inputActiveBackground.toString()}; }`);
}
// This rule is used to override the outline color for synthetic-focus find input.
const focusOutline = theme.getColor(focusBorder);
if (focusOutline) {
collector.addRule(`.monaco-workbench .monaco-editor .find-widget .monaco-inputbox.synthetic-focus { outline-color: ${focusOutline}; }`);
}
});

View File

@@ -52,7 +52,7 @@ export class ParameterHintsModel extends Disposable {
public readonly onChangedHints = this._onChangedHints.event;
private readonly editor: ICodeEditor;
private enabled: boolean;
private triggerOnType = false;
private _state: ParameterHintState.State = ParameterHintState.Default;
private readonly _lastSignatureHelpResult = this._register(new MutableDisposable<modes.SignatureHelpResult>());
private triggerChars = new CharacterSet();
@@ -68,7 +68,6 @@ export class ParameterHintsModel extends Disposable {
super();
this.editor = editor;
this.enabled = false;
this.throttledDelayer = new Delayer(delay);
@@ -242,7 +241,7 @@ export class ParameterHintsModel extends Disposable {
}
private onDidType(text: string) {
if (!this.enabled) {
if (!this.triggerOnType) {
return;
}
@@ -272,9 +271,9 @@ export class ParameterHintsModel extends Disposable {
}
private onEditorConfigurationChange(): void {
this.enabled = this.editor.getConfiguration().contribInfo.parameterHints.enabled;
this.triggerOnType = this.editor.getConfiguration().contribInfo.parameterHints.enabled;
if (!this.enabled) {
if (!this.triggerOnType) {
this.cancel();
}
}
@@ -283,4 +282,4 @@ export class ParameterHintsModel extends Disposable {
this.cancel(true);
super.dispose();
}
}
}

View File

@@ -97,6 +97,10 @@
font-weight: bold;
}
.monaco-editor .suggest-widget-deprecated span {
text-decoration: line-through;
}
/** Icon styles **/
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .close,

View File

@@ -30,7 +30,7 @@ import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { TimeoutTimer, CancelablePromise, createCancelablePromise, disposableTimeout } from 'vs/base/common/async';
import { CompletionItemKind, completionKindToCssClass } from 'vs/editor/common/modes';
import { CompletionItemKind, completionKindToCssClass, CompletionItemKindModifier } from 'vs/editor/common/modes';
import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { getIconClasses } from 'vs/editor/common/services/getIconClasses';
import { IModelService } from 'vs/editor/common/services/modelService';
@@ -38,6 +38,7 @@ import { URI } from 'vs/base/common/uri';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { FileKind } from 'vs/platform/files/common/files';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { flatten } from 'vs/base/common/arrays';
const expandSuggestionDocsByDefault = false;
@@ -60,7 +61,6 @@ export const editorSuggestWidgetForeground = registerColor('editorSuggestWidget.
export const editorSuggestWidgetSelectedBackground = registerColor('editorSuggestWidget.selectedBackground', { dark: listFocusBackground, light: listFocusBackground, hc: listFocusBackground }, nls.localize('editorSuggestWidgetSelectedBackground', 'Background color of the selected entry in the suggest widget.'));
export const editorSuggestWidgetHighlightForeground = registerColor('editorSuggestWidget.highlightForeground', { dark: listHighlightForeground, light: listHighlightForeground, hc: listHighlightForeground }, nls.localize('editorSuggestWidgetHighlightForeground', 'Color of the match highlights in the suggest widget.'));
const colorRegExp = /^(#([\da-f]{3}){1,2}|(rgb|hsl)a\(\s*(\d{1,3}%?\s*,\s*){3}(1|0?\.\d+)\)|(rgb|hsl)\(\s*\d{1,3}%?(\s*,\s*\d{1,3}%?){2}\s*\))$/i;
function extractColor(item: CompletionItem, out: string[]): boolean {
if (item.completion.label.match(colorRegExp)) {
@@ -173,18 +173,18 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
} else if (suggestion.kind === CompletionItemKind.File && this._themeService.getIconTheme().hasFileIcons) {
// special logic for 'file' completion items
data.icon.className = 'icon hide';
labelOptions.extraClasses = ([] as string[]).concat(
labelOptions.extraClasses = flatten([
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.label }), FileKind.FILE),
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.detail }), FileKind.FILE)
);
]);
} else if (suggestion.kind === CompletionItemKind.Folder && this._themeService.getIconTheme().hasFolderIcons) {
// special logic for 'folder' completion items
data.icon.className = 'icon hide';
labelOptions.extraClasses = ([] as string[]).concat(
labelOptions.extraClasses = flatten([
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.label }), FileKind.FOLDER),
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.detail }), FileKind.FOLDER)
);
]);
} else {
// normal icon
data.icon.className = 'icon hide';
@@ -193,6 +193,10 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
];
}
if (suggestion.kindModifier && suggestion.kindModifier & CompletionItemKindModifier.Deprecated) {
labelOptions.extraClasses = (labelOptions.extraClasses || []).concat(['suggest-widget-deprecated']);
}
data.iconLabel.setLabel(suggestion.label, undefined, labelOptions);
data.typeLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, '');