/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
import './hover.css';
import * as nls from '../../../nls';
import { KeyChord } from '../../../base/common/keyCodes';
import { DisposableStore, MutableDisposable } from '../../../base/common/lifecycle';
import { EditorAction, registerEditorAction, registerEditorContribution } from '../../browser/editorExtensions';
import { Range } from '../../common/core/range';
import { EditorContextKeys } from '../../common/editorContextKeys';
import { IModeService } from '../../common/services/modeService';
import { ModesContentHoverWidget } from './modesContentHover';
import { ModesGlyphHoverWidget } from './modesGlyphHover';
import { IOpenerService } from '../../../platform/opener/common/opener';
import { editorHoverBackground, editorHoverBorder, editorHoverHighlight, textCodeBlockBackground, textLinkForeground, editorHoverStatusBarBackground, editorHoverForeground } from '../../../platform/theme/common/colorRegistry';
import { IThemeService, registerThemingParticipant } from '../../../platform/theme/common/themeService';
import { IMarkerDecorationsService } from '../../common/services/markersDecorationService';
import { IKeybindingService } from '../../../platform/keybinding/common/keybinding';
import { GotoDefinitionAtPositionEditorContribution } from '../gotoSymbol/link/goToDefinitionAtPosition';
var ModesHoverController = /** @class */ (function () {
    function ModesHoverController(_editor, _openerService, _modeService, _markerDecorationsService, _keybindingService, _themeService) {
        var _this = this;
        this._editor = _editor;
        this._openerService = _openerService;
        this._modeService = _modeService;
        this._markerDecorationsService = _markerDecorationsService;
        this._keybindingService = _keybindingService;
        this._themeService = _themeService;
        this._toUnhook = new DisposableStore();
        this._contentWidget = new MutableDisposable();
        this._glyphWidget = new MutableDisposable();
        this._isMouseDown = false;
        this._hoverClicked = false;
        this._hookEvents();
        this._didChangeConfigurationHandler = this._editor.onDidChangeConfiguration(function (e) {
            if (e.hasChanged(44 /* hover */)) {
                _this._hideWidgets();
                _this._unhookEvents();
                _this._hookEvents();
            }
        });
    }
    Object.defineProperty(ModesHoverController.prototype, "contentWidget", {
        get: function () {
            if (!this._contentWidget.value) {
                this._createHoverWidgets();
            }
            return this._contentWidget.value;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ModesHoverController.prototype, "glyphWidget", {
        get: function () {
            if (!this._glyphWidget.value) {
                this._createHoverWidgets();
            }
            return this._glyphWidget.value;
        },
        enumerable: true,
        configurable: true
    });
    ModesHoverController.get = function (editor) {
        return editor.getContribution(ModesHoverController.ID);
    };
    ModesHoverController.prototype._hookEvents = function () {
        var _this = this;
        var hideWidgetsEventHandler = function () { return _this._hideWidgets(); };
        var hoverOpts = this._editor.getOption(44 /* hover */);
        this._isHoverEnabled = hoverOpts.enabled;
        this._isHoverSticky = hoverOpts.sticky;
        if (this._isHoverEnabled) {
            this._toUnhook.add(this._editor.onMouseDown(function (e) { return _this._onEditorMouseDown(e); }));
            this._toUnhook.add(this._editor.onMouseUp(function (e) { return _this._onEditorMouseUp(e); }));
            this._toUnhook.add(this._editor.onMouseMove(function (e) { return _this._onEditorMouseMove(e); }));
            this._toUnhook.add(this._editor.onKeyDown(function (e) { return _this._onKeyDown(e); }));
            this._toUnhook.add(this._editor.onDidChangeModelDecorations(function () { return _this._onModelDecorationsChanged(); }));
        }
        else {
            this._toUnhook.add(this._editor.onMouseMove(hideWidgetsEventHandler));
        }
        this._toUnhook.add(this._editor.onMouseLeave(hideWidgetsEventHandler));
        this._toUnhook.add(this._editor.onDidChangeModel(hideWidgetsEventHandler));
        this._toUnhook.add(this._editor.onDidScrollChange(function (e) { return _this._onEditorScrollChanged(e); }));
    };
    ModesHoverController.prototype._unhookEvents = function () {
        this._toUnhook.clear();
    };
    ModesHoverController.prototype._onModelDecorationsChanged = function () {
        this.contentWidget.onModelDecorationsChanged();
        this.glyphWidget.onModelDecorationsChanged();
    };
    ModesHoverController.prototype._onEditorScrollChanged = function (e) {
        if (e.scrollTopChanged || e.scrollLeftChanged) {
            this._hideWidgets();
        }
    };
    ModesHoverController.prototype._onEditorMouseDown = function (mouseEvent) {
        this._isMouseDown = true;
        var targetType = mouseEvent.target.type;
        if (targetType === 9 /* CONTENT_WIDGET */ && mouseEvent.target.detail === ModesContentHoverWidget.ID) {
            this._hoverClicked = true;
            // mouse down on top of content hover widget
            return;
        }
        if (targetType === 12 /* OVERLAY_WIDGET */ && mouseEvent.target.detail === ModesGlyphHoverWidget.ID) {
            // mouse down on top of overlay hover widget
            return;
        }
        if (targetType !== 12 /* OVERLAY_WIDGET */ && mouseEvent.target.detail !== ModesGlyphHoverWidget.ID) {
            this._hoverClicked = false;
        }
        this._hideWidgets();
    };
    ModesHoverController.prototype._onEditorMouseUp = function (mouseEvent) {
        this._isMouseDown = false;
    };
    ModesHoverController.prototype._onEditorMouseMove = function (mouseEvent) {
        var targetType = mouseEvent.target.type;
        if (this._isMouseDown && this._hoverClicked && this.contentWidget.isColorPickerVisible()) {
            return;
        }
        if (this._isHoverSticky && targetType === 9 /* CONTENT_WIDGET */ && mouseEvent.target.detail === ModesContentHoverWidget.ID) {
            // mouse moved on top of content hover widget
            return;
        }
        if (this._isHoverSticky && targetType === 12 /* OVERLAY_WIDGET */ && mouseEvent.target.detail === ModesGlyphHoverWidget.ID) {
            // mouse moved on top of overlay hover widget
            return;
        }
        if (targetType === 7 /* CONTENT_EMPTY */) {
            var epsilon = this._editor.getOption(34 /* fontInfo */).typicalHalfwidthCharacterWidth / 2;
            var data = mouseEvent.target.detail;
            if (data && !data.isAfterLines && typeof data.horizontalDistanceToText === 'number' && data.horizontalDistanceToText < epsilon) {
                // Let hover kick in even when the mouse is technically in the empty area after a line, given the distance is small enough
                targetType = 6 /* CONTENT_TEXT */;
            }
        }
        if (targetType === 6 /* CONTENT_TEXT */) {
            this.glyphWidget.hide();
            if (this._isHoverEnabled && mouseEvent.target.range) {
                this.contentWidget.startShowingAt(mouseEvent.target.range, 0 /* Delayed */, false);
            }
        }
        else if (targetType === 2 /* GUTTER_GLYPH_MARGIN */) {
            this.contentWidget.hide();
            if (this._isHoverEnabled && mouseEvent.target.position) {
                this.glyphWidget.startShowingAt(mouseEvent.target.position.lineNumber);
            }
        }
        else {
            this._hideWidgets();
        }
    };
    ModesHoverController.prototype._onKeyDown = function (e) {
        if (e.keyCode !== 5 /* Ctrl */ && e.keyCode !== 6 /* Alt */ && e.keyCode !== 57 /* Meta */ && e.keyCode !== 4 /* Shift */) {
            // Do not hide hover when a modifier key is pressed
            this._hideWidgets();
        }
    };
    ModesHoverController.prototype._hideWidgets = function () {
        if (!this._glyphWidget.value || !this._contentWidget.value || (this._isMouseDown && this._hoverClicked && this._contentWidget.value.isColorPickerVisible())) {
            return;
        }
        this._glyphWidget.value.hide();
        this._contentWidget.value.hide();
    };
    ModesHoverController.prototype._createHoverWidgets = function () {
        this._contentWidget.value = new ModesContentHoverWidget(this._editor, this._markerDecorationsService, this._themeService, this._keybindingService, this._modeService, this._openerService);
        this._glyphWidget.value = new ModesGlyphHoverWidget(this._editor, this._modeService, this._openerService);
    };
    ModesHoverController.prototype.showContentHover = function (range, mode, focus) {
        this.contentWidget.startShowingAt(range, mode, focus);
    };
    ModesHoverController.prototype.dispose = function () {
        this._unhookEvents();
        this._toUnhook.dispose();
        this._didChangeConfigurationHandler.dispose();
        this._glyphWidget.dispose();
        this._contentWidget.dispose();
    };
    ModesHoverController.ID = 'editor.contrib.hover';
    ModesHoverController = __decorate([
        __param(1, IOpenerService),
        __param(2, IModeService),
        __param(3, IMarkerDecorationsService),
        __param(4, IKeybindingService),
        __param(5, IThemeService)
    ], ModesHoverController);
    return ModesHoverController;
}());
export { ModesHoverController };
var ShowHoverAction = /** @class */ (function (_super) {
    __extends(ShowHoverAction, _super);
    function ShowHoverAction() {
        return _super.call(this, {
            id: 'editor.action.showHover',
            label: nls.localize({
                key: 'showHover',
                comment: [
                    'Label for action that will trigger the showing of a hover in the editor.',
                    'This allows for users to show the hover without using the mouse.'
                ]
            }, "Show Hover"),
            alias: 'Show Hover',
            precondition: undefined,
            kbOpts: {
                kbExpr: EditorContextKeys.editorTextFocus,
                primary: KeyChord(2048 /* CtrlCmd */ | 41 /* KEY_K */, 2048 /* CtrlCmd */ | 39 /* KEY_I */),
                weight: 100 /* EditorContrib */
            }
        }) || this;
    }
    ShowHoverAction.prototype.run = function (accessor, editor) {
        if (!editor.hasModel()) {
            return;
        }
        var controller = ModesHoverController.get(editor);
        if (!controller) {
            return;
        }
        var position = editor.getPosition();
        var range = new Range(position.lineNumber, position.column, position.lineNumber, position.column);
        var focus = editor.getOption(2 /* accessibilitySupport */) === 2 /* Enabled */;
        controller.showContentHover(range, 1 /* Immediate */, focus);
    };
    return ShowHoverAction;
}(EditorAction));
var ShowDefinitionPreviewHoverAction = /** @class */ (function (_super) {
    __extends(ShowDefinitionPreviewHoverAction, _super);
    function ShowDefinitionPreviewHoverAction() {
        return _super.call(this, {
            id: 'editor.action.showDefinitionPreviewHover',
            label: nls.localize({
                key: 'showDefinitionPreviewHover',
                comment: [
                    'Label for action that will trigger the showing of definition preview hover in the editor.',
                    'This allows for users to show the definition preview hover without using the mouse.'
                ]
            }, "Show Definition Preview Hover"),
            alias: 'Show Definition Preview Hover',
            precondition: undefined
        }) || this;
    }
    ShowDefinitionPreviewHoverAction.prototype.run = function (accessor, editor) {
        var controller = ModesHoverController.get(editor);
        if (!controller) {
            return;
        }
        var position = editor.getPosition();
        if (!position) {
            return;
        }
        var range = new Range(position.lineNumber, position.column, position.lineNumber, position.column);
        var goto = GotoDefinitionAtPositionEditorContribution.get(editor);
        var promise = goto.startFindDefinitionFromCursor(position);
        if (promise) {
            promise.then(function () {
                controller.showContentHover(range, 1 /* Immediate */, true);
            });
        }
        else {
            controller.showContentHover(range, 1 /* Immediate */, true);
        }
    };
    return ShowDefinitionPreviewHoverAction;
}(EditorAction));
registerEditorContribution(ModesHoverController.ID, ModesHoverController);
registerEditorAction(ShowHoverAction);
registerEditorAction(ShowDefinitionPreviewHoverAction);
// theming
registerThemingParticipant(function (theme, collector) {
    var editorHoverHighlightColor = theme.getColor(editorHoverHighlight);
    if (editorHoverHighlightColor) {
        collector.addRule(".monaco-editor .hoverHighlight { background-color: " + editorHoverHighlightColor + "; }");
    }
    var hoverBackground = theme.getColor(editorHoverBackground);
    if (hoverBackground) {
        collector.addRule(".monaco-editor .monaco-editor-hover { background-color: " + hoverBackground + "; }");
    }
    var hoverBorder = theme.getColor(editorHoverBorder);
    if (hoverBorder) {
        collector.addRule(".monaco-editor .monaco-editor-hover { border: 1px solid " + hoverBorder + "; }");
        collector.addRule(".monaco-editor .monaco-editor-hover .hover-row:not(:first-child):not(:empty) { border-top: 1px solid " + hoverBorder.transparent(0.5) + "; }");
        collector.addRule(".monaco-editor .monaco-editor-hover hr { border-top: 1px solid " + hoverBorder.transparent(0.5) + "; }");
        collector.addRule(".monaco-editor .monaco-editor-hover hr { border-bottom: 0px solid " + hoverBorder.transparent(0.5) + "; }");
    }
    var link = theme.getColor(textLinkForeground);
    if (link) {
        collector.addRule(".monaco-editor .monaco-editor-hover a { color: " + link + "; }");
    }
    var hoverForeground = theme.getColor(editorHoverForeground);
    if (hoverForeground) {
        collector.addRule(".monaco-editor .monaco-editor-hover { color: " + hoverForeground + "; }");
    }
    var actionsBackground = theme.getColor(editorHoverStatusBarBackground);
    if (actionsBackground) {
        collector.addRule(".monaco-editor .monaco-editor-hover .hover-row .actions { background-color: " + actionsBackground + "; }");
    }
    var codeBackground = theme.getColor(textCodeBlockBackground);
    if (codeBackground) {
        collector.addRule(".monaco-editor .monaco-editor-hover code { background-color: " + codeBackground + "; }");
    }
});
