/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';
var __extends = (this && this.__extends) || (function () {
    var 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 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); }
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [0, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
import * as nls from '../../../nls';
import { dispose } from '../../../base/common/lifecycle';
import { ICommandService } from '../../../platform/commands/common/commands';
import { IContextMenuService } from '../../../platform/contextview/browser/contextView';
import { ContextKeyExpr, IContextKeyService } from '../../../platform/contextkey/common/contextkey';
import { IKeybindingService } from '../../../platform/keybinding/common/keybinding';
import { optional } from '../../../platform/instantiation/common/instantiation';
import { IMarkerService } from '../../../platform/markers/common/markers';
import { EditorContextKeys } from '../../common/editorContextKeys';
import { registerEditorAction, registerEditorContribution, EditorAction, EditorCommand, registerEditorCommand } from '../../browser/editorExtensions';
import { QuickFixContextMenu } from './quickFixWidget';
import { LightBulbWidget } from './lightBulbWidget';
import { QuickFixModel } from './quickFixModel';
import { CodeActionKind, CodeActionAutoApply } from './codeActionTrigger';
import { TPromise } from '../../../base/common/winjs.base';
import { BulkEdit } from '../../browser/services/bulkEdit';
import { IFileService } from '../../../platform/files/common/files';
import { ITextModelService } from '../../common/services/resolverService';
var QuickFixController = /** @class */ (function () {
    function QuickFixController(editor, markerService, contextKeyService, _commandService, contextMenuService, _keybindingService, _textModelService, _fileService) {
        var _this = this;
        this._commandService = _commandService;
        this._keybindingService = _keybindingService;
        this._textModelService = _textModelService;
        this._fileService = _fileService;
        this._disposables = [];
        this._editor = editor;
        this._model = new QuickFixModel(this._editor, markerService);
        this._quickFixContextMenu = new QuickFixContextMenu(editor, contextMenuService, function (action) { return _this._onApplyCodeAction(action); });
        this._lightBulbWidget = new LightBulbWidget(editor);
        this._updateLightBulbTitle();
        this._disposables.push(this._quickFixContextMenu.onDidExecuteCodeAction(function (_) { return _this._model.trigger({ type: 'auto' }); }), this._lightBulbWidget.onClick(this._handleLightBulbSelect, this), this._model.onDidChangeFixes(function (e) { return _this._onQuickFixEvent(e); }), this._keybindingService.onDidUpdateKeybindings(this._updateLightBulbTitle, this));
    }
    QuickFixController.get = function (editor) {
        return editor.getContribution(QuickFixController.ID);
    };
    QuickFixController.prototype.dispose = function () {
        this._model.dispose();
        dispose(this._disposables);
    };
    QuickFixController.prototype._onQuickFixEvent = function (e) {
        var _this = this;
        if (e && e.trigger.kind) {
            // Triggered for specific scope
            // Apply if we only have one action or requested autoApply, otherwise show menu
            e.fixes.then(function (fixes) {
                if (e.trigger.autoApply === CodeActionAutoApply.First || (e.trigger.autoApply === CodeActionAutoApply.IfSingle && fixes.length === 1)) {
                    _this._onApplyCodeAction(fixes[0]);
                }
                else {
                    _this._quickFixContextMenu.show(e.fixes, e.position);
                }
            });
            return;
        }
        if (e && e.trigger.type === 'manual') {
            this._quickFixContextMenu.show(e.fixes, e.position);
        }
        else if (e && e.fixes) {
            // auto magically triggered
            // * update an existing list of code actions
            // * manage light bulb
            if (this._quickFixContextMenu.isVisible) {
                this._quickFixContextMenu.show(e.fixes, e.position);
            }
            else {
                this._lightBulbWidget.model = e;
            }
        }
        else {
            this._lightBulbWidget.hide();
        }
    };
    QuickFixController.prototype.getId = function () {
        return QuickFixController.ID;
    };
    QuickFixController.prototype._handleLightBulbSelect = function (coords) {
        this._quickFixContextMenu.show(this._lightBulbWidget.model.fixes, coords);
    };
    QuickFixController.prototype.triggerFromEditorSelection = function () {
        this._model.trigger({ type: 'manual' });
    };
    QuickFixController.prototype.triggerCodeActionFromEditorSelection = function (kind, autoApply) {
        this._model.trigger({ type: 'manual', kind: kind, autoApply: autoApply });
    };
    QuickFixController.prototype._updateLightBulbTitle = function () {
        var kb = this._keybindingService.lookupKeybinding(QuickFixAction.Id);
        var title;
        if (kb) {
            title = nls.localize('quickFixWithKb', "Show Fixes ({0})", kb.getLabel());
        }
        else {
            title = nls.localize('quickFix', "Show Fixes");
        }
        this._lightBulbWidget.title = title;
    };
    QuickFixController.prototype._onApplyCodeAction = function (action) {
        return __awaiter(this, void 0, TPromise, function () {
            var _a;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        if (!action.edit) return [3 /*break*/, 2];
                        return [4 /*yield*/, BulkEdit.perform(action.edit.edits, this._textModelService, this._fileService, this._editor)];
                    case 1:
                        _b.sent();
                        _b.label = 2;
                    case 2:
                        if (!action.command) return [3 /*break*/, 4];
                        return [4 /*yield*/, (_a = this._commandService).executeCommand.apply(_a, [action.command.id].concat(action.command.arguments))];
                    case 3:
                        _b.sent();
                        _b.label = 4;
                    case 4: return [2 /*return*/];
                }
            });
        });
    };
    QuickFixController.ID = 'editor.contrib.quickFixController';
    QuickFixController = __decorate([
        __param(1, IMarkerService),
        __param(2, IContextKeyService),
        __param(3, ICommandService),
        __param(4, IContextMenuService),
        __param(5, IKeybindingService),
        __param(6, ITextModelService),
        __param(7, optional(IFileService))
    ], QuickFixController);
    return QuickFixController;
}());
export { QuickFixController };
var QuickFixAction = /** @class */ (function (_super) {
    __extends(QuickFixAction, _super);
    function QuickFixAction() {
        return _super.call(this, {
            id: QuickFixAction.Id,
            label: nls.localize('quickfix.trigger.label', "Quick Fix"),
            alias: 'Quick Fix',
            precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider),
            kbOpts: {
                kbExpr: EditorContextKeys.textFocus,
                primary: 2048 /* CtrlCmd */ | 84 /* US_DOT */
            }
        }) || this;
    }
    QuickFixAction.prototype.run = function (accessor, editor) {
        var controller = QuickFixController.get(editor);
        if (controller) {
            controller.triggerFromEditorSelection();
        }
    };
    QuickFixAction.Id = 'editor.action.quickFix';
    return QuickFixAction;
}(EditorAction));
export { QuickFixAction };
var CodeActionCommandArgs = /** @class */ (function () {
    function CodeActionCommandArgs(kind, apply) {
        this.kind = kind;
        this.apply = apply;
    }
    CodeActionCommandArgs.fromUser = function (arg) {
        if (!arg || typeof arg !== 'object') {
            return new CodeActionCommandArgs(CodeActionKind.Empty, CodeActionAutoApply.IfSingle);
        }
        return new CodeActionCommandArgs(CodeActionCommandArgs.getKindFromUser(arg), CodeActionCommandArgs.getApplyFromUser(arg));
    };
    CodeActionCommandArgs.getApplyFromUser = function (arg) {
        switch (typeof arg.apply === 'string' ? arg.apply.toLowerCase() : '') {
            case 'first':
                return CodeActionAutoApply.First;
            case 'never':
                return CodeActionAutoApply.Never;
            case 'ifsingle':
            default:
                return CodeActionAutoApply.IfSingle;
        }
    };
    CodeActionCommandArgs.getKindFromUser = function (arg) {
        return typeof arg.kind === 'string'
            ? new CodeActionKind(arg.kind)
            : CodeActionKind.Empty;
    };
    return CodeActionCommandArgs;
}());
var CodeActionCommand = /** @class */ (function (_super) {
    __extends(CodeActionCommand, _super);
    function CodeActionCommand() {
        return _super.call(this, {
            id: CodeActionCommand.Id,
            precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider)
        }) || this;
    }
    CodeActionCommand.prototype.runEditorCommand = function (accessor, editor, userArg) {
        var controller = QuickFixController.get(editor);
        if (controller) {
            var args = CodeActionCommandArgs.fromUser(userArg);
            controller.triggerCodeActionFromEditorSelection(args.kind, args.apply);
        }
    };
    CodeActionCommand.Id = 'editor.action.codeAction';
    return CodeActionCommand;
}(EditorCommand));
export { CodeActionCommand };
var RefactorAction = /** @class */ (function (_super) {
    __extends(RefactorAction, _super);
    function RefactorAction() {
        return _super.call(this, {
            id: RefactorAction.Id,
            label: nls.localize('refactor.label', "Refactor"),
            alias: 'Refactor',
            precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider),
            kbOpts: {
                kbExpr: EditorContextKeys.textFocus,
                primary: 256 /* WinCtrl */ | 1024 /* Shift */ | 48 /* KEY_R */
            }
        }) || this;
    }
    RefactorAction.prototype.run = function (accessor, editor) {
        var controller = QuickFixController.get(editor);
        if (controller) {
            controller.triggerCodeActionFromEditorSelection(CodeActionKind.Refactor, CodeActionAutoApply.Never);
        }
    };
    RefactorAction.Id = 'editor.action.refactor';
    return RefactorAction;
}(EditorAction));
export { RefactorAction };
registerEditorContribution(QuickFixController);
registerEditorAction(QuickFixAction);
registerEditorAction(RefactorAction);
registerEditorCommand(new CodeActionCommand());
