fix #21064, also support dismiss on ESC and 3s timeout, move out code to its own file, tweak message

This commit is contained in:
Johannes Rieken
2017-02-23 12:55:22 +01:00
parent 973bac0c4f
commit 85b3aa52fa
5 changed files with 191 additions and 102 deletions
+7 -2
View File
@@ -9,7 +9,7 @@ import * as errors from 'vs/base/common/errors';
import * as platform from 'vs/base/common/platform';
import { Promise, TPromise, ValueCallback, ErrorCallback, ProgressCallback } from 'vs/base/common/winjs.base';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { Disposable } from 'vs/base/common/lifecycle';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import Event, { Emitter } from 'vs/base/common/event';
function isThenable<T>(obj: any): obj is Thenable<T> {
@@ -501,6 +501,11 @@ export class Queue<T> extends Limiter<T> {
}
}
export function setDisposableTimeout(handler: Function, timeout: number, ...args: any[]): IDisposable {
const handle = setTimeout(handler, timeout, ...args);
return { dispose() { clearTimeout(handle); } };
}
export class TimeoutTimer extends Disposable {
private _token: platform.TimeoutToken;
@@ -642,4 +647,4 @@ export function ninvoke(thisArg: any, fn: Function, ...args: any[]): Promise;
export function ninvoke<T>(thisArg: any, fn: Function, ...args: any[]): TPromise<T>;
export function ninvoke(thisArg: any, fn: Function, ...args: any[]): any {
return new Promise((c, e) => fn.call(thisArg, ...args, (err, result) => err ? e(err) : c(result)));
}
}
@@ -3,27 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-editor .monaco-editor-overlaymessage {
padding-bottom: 8px;
}
.monaco-editor .monaco-editor-overlaymessage .message {
padding: 1px 4px;
background: #1382CE;
color: white;
}
.monaco-editor .monaco-editor-overlaymessage .anchor {
width: 0 !important;
height: 0 !important;
border-color: transparent;
border-style: solid;
z-index: 1000;
border-width: 8px;
border-top-color: #1382CE;
position: absolute;
}
.monaco-editor .preview-zone-widget.preview .monaco-editor,
.monaco-editor .preview-zone-widget.preview .monaco-editor-background,
.monaco-editor .preview-zone-widget.preview .monaco-editor .line-numbers,
@@ -8,7 +8,6 @@
import 'vs/css!./goToDeclaration';
import * as nls from 'vs/nls';
import { Throttler } from 'vs/base/common/async';
import { any, filterEvent } from 'vs/base/common/event';
import { onUnexpectedError } from 'vs/base/common/errors';
import { MarkedString } from 'vs/base/common/htmlContent';
import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes';
@@ -25,7 +24,7 @@ import { Range } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { editorAction, IActionOptions, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions';
import { Location, DefinitionProviderRegistry } from 'vs/editor/common/modes';
import { ICodeEditor, IEditorMouseEvent, IMouseTarget, IContentWidget, IContentWidgetPosition, ContentWidgetPositionPreference } from 'vs/editor/browser/editorBrowser';
import { ICodeEditor, IEditorMouseEvent, IMouseTarget } from 'vs/editor/browser/editorBrowser';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition } from 'vs/editor/contrib/goToDeclaration/common/goToDeclaration';
import { ReferencesController } from 'vs/editor/contrib/referenceSearch/browser/referencesController';
@@ -34,91 +33,17 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { PeekContext } from 'vs/editor/contrib/zoneWidget/browser/peekViewWidget';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ITextModelResolverService } from 'vs/editor/common/services/resolverService';
import { MessageController } from './messageController';
import * as corePosition from 'vs/editor/common/core/position';
import ModeContextKeys = editorCommon.ModeContextKeys;
import EditorContextKeys = editorCommon.EditorContextKeys;
class MessageOverlay implements IContentWidget {
private static _last: IDisposable;
static show(editor: ICodeEditor, pos: editorCommon.IPosition, message: string): void {
dispose(MessageOverlay._last);
const widget = new MessageOverlay(message, pos.lineNumber, editor.getOffsetForColumn(pos.lineNumber, pos.column));
const remove = () => editor.removeContentWidget(widget);
editor.addContentWidget(widget);
const listener = any<any>(
filterEvent(editor.onMouseMove, e => {
const { position } = e.target;
return Math.abs(position.lineNumber - pos.lineNumber) > 1
|| Math.abs(position.column - pos.column) > 10;
}),
editor.onDidChangeCursorPosition,
editor.onDidBlurEditorText,
editor.onDidDispose,
editor.onDidChangeModel
)(_ => {
listener.dispose();
remove();
});
MessageOverlay._last = {
dispose() {
listener.dispose();
remove();
}
};
}
readonly allowEditorOverflow = true;
readonly suppressMouseDown = false;
private _position: editorCommon.IPosition;
private _domNode: HTMLDivElement;
constructor(text: string, lineNumber: number, horizontalOffset: number) {
this._position = { lineNumber, column: 1 };
this._domNode = document.createElement('div');
this._domNode.style.paddingLeft = `${horizontalOffset - 6}px`;
this._domNode.classList.add('monaco-editor-overlaymessage');
const message = document.createElement('div');
message.classList.add('message');
message.textContent = text;
this._domNode.appendChild(message);
const anchor = document.createElement('div');
anchor.classList.add('anchor');
this._domNode.appendChild(anchor);
}
getId(): string {
return 'messageoverlay';
}
getDomNode(): HTMLElement {
return this._domNode;
}
getPosition(): IContentWidgetPosition {
return { position: this._position, preference: [ContentWidgetPositionPreference.ABOVE] };
}
}
export class DefinitionActionConfig {
constructor(
public openToSide = false,
public openInPeek = false,
public filterCurrent = true,
public noResultsMessage = nls.localize('generic.noResults', "No definition found")
public filterCurrent = true
) {
//
}
@@ -164,7 +89,12 @@ export class DefinitionAction extends EditorAction {
}
if (result.length === 0) {
MessageOverlay.show(<ICodeEditor>editor, pos, this._configuration.noResultsMessage);
const info = model.getWordAtPosition(pos);
const message = info && info.word
? nls.localize('noResultWord', "No definition found for '{0}'", info.word)
: nls.localize('generic.noResults', "No definition found");
MessageController.get(editor).showMessage(message, pos);
return;
}
@@ -0,0 +1,25 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-editor .monaco-editor-overlaymessage {
padding-bottom: 8px;
}
.monaco-editor .monaco-editor-overlaymessage .message {
padding: 1px 4px;
background: #1382CE;
color: white;
}
.monaco-editor .monaco-editor-overlaymessage .anchor {
width: 0 !important;
height: 0 !important;
border-color: transparent;
border-style: solid;
z-index: 1000;
border-width: 8px;
border-top-color: #1382CE;
position: absolute;
}
@@ -0,0 +1,150 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./messageController';
import { any } from 'vs/base/common/event';
import { setDisposableTimeout } from 'vs/base/common/async';
import { KeyCode } from 'vs/base/common/keyCodes';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Range } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { commonEditorContribution, CommonEditorRegistry, EditorCommand } from 'vs/editor/common/editorCommonExtensions';
import { ICodeEditor, IContentWidget, IContentWidgetPosition, ContentWidgetPositionPreference } from 'vs/editor/browser/editorBrowser';
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
@commonEditorContribution
export class MessageController {
private static _id = 'editor.contrib.messageController';
static CONTEXT_SNIPPET_MODE = new RawContextKey<boolean>('messageVisible', false);
static get(editor: editorCommon.ICommonCodeEditor): MessageController {
return editor.getContribution<MessageController>(MessageController._id);
}
getId(): string {
return MessageController._id;
}
private _editor: ICodeEditor;
private _visible: IContextKey<boolean>;
private _messageListeners: IDisposable[] = [];
constructor(
editor: ICodeEditor,
@IContextKeyService contextKeyService: IContextKeyService
) {
this._editor = editor;
this._visible = MessageController.CONTEXT_SNIPPET_MODE.bindTo(contextKeyService);
}
dispose() {
this._visible.reset();
}
showMessage(message: string, position: editorCommon.IPosition): void {
this._visible.set(true);
this._messageListeners = dispose(this._messageListeners);
this._messageListeners.push(new MessageWidget(this._editor, position, message));
// close on blur, cursor, model change, dispose
this._messageListeners.push(any<any>(
this._editor.onDidBlurEditorText,
this._editor.onDidChangeCursorPosition,
this._editor.onDidDispose,
this._editor.onDidChangeModel
)(this.closeMessage, this));
// close after 3s
this._messageListeners.push(setDisposableTimeout(() => this.closeMessage(), 3000));
// close on mouse move
let bounds: Range;
this._messageListeners.push(this._editor.onMouseMove(e => {
// outside the text area
if (!e.target.position) {
return;
}
if (!bounds) {
// define bounding box around position and first mouse occurance
bounds = new Range(position.lineNumber - 3, 1, e.target.position.lineNumber + 3, 1);
} else if (!bounds.containsPosition(e.target.position)) {
// check if position is still in bounds
this.closeMessage();
}
}));
}
closeMessage(): void {
this._visible.reset();
this._messageListeners = dispose(this._messageListeners);
}
}
const MessageCommand = EditorCommand.bindToContribution<MessageController>(MessageController.get);
CommonEditorRegistry.registerEditorCommand(new MessageCommand({
id: 'leaveEditorMessage',
precondition: MessageController.CONTEXT_SNIPPET_MODE,
handler: c => c.closeMessage(),
kbOpts: {
weight: CommonEditorRegistry.commandWeight(30),
primary: KeyCode.Escape
}
}));
class MessageWidget implements IContentWidget {
readonly allowEditorOverflow = true;
readonly suppressMouseDown = false;
private _editor: ICodeEditor;
private _position: editorCommon.IPosition;
private _domNode: HTMLDivElement;
constructor(editor: ICodeEditor, { lineNumber, column }: editorCommon.IPosition, text: string) {
this._editor = editor;
this._editor.revealLinesInCenterIfOutsideViewport(lineNumber, lineNumber);
this._position = { lineNumber, column: 1 };
this._domNode = document.createElement('div');
this._domNode.style.paddingLeft = `${editor.getOffsetForColumn(lineNumber, column) - 6}px`;
this._domNode.classList.add('monaco-editor-overlaymessage');
const message = document.createElement('div');
message.classList.add('message');
message.textContent = text;
this._domNode.appendChild(message);
const anchor = document.createElement('div');
anchor.classList.add('anchor');
this._domNode.appendChild(anchor);
this._editor.addContentWidget(this);
}
dispose() {
this._editor.removeContentWidget(this);
}
getId(): string {
return 'messageoverlay';
}
getDomNode(): HTMLElement {
return this._domNode;
}
getPosition(): IContentWidgetPosition {
return { position: this._position, preference: [ContentWidgetPositionPreference.ABOVE] };
}
}