/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 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) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import * as nls from '../../../nls.js'; import * as dom from '../../../base/browser/dom.js'; import { Action } from '../../../base/common/actions.js'; import { Disposable } from '../../../base/common/lifecycle.js'; import { Range } from '../../common/core/range.js'; import { Codicon } from '../../../base/common/codicons.js'; export class InlineDiffMargin extends Disposable { constructor(_viewZoneId, _marginDomNode, editor, diff, _contextMenuService, _clipboardService) { super(); this._viewZoneId = _viewZoneId; this._marginDomNode = _marginDomNode; this.editor = editor; this.diff = diff; this._contextMenuService = _contextMenuService; this._clipboardService = _clipboardService; this._visibility = false; // make sure the diff margin shows above overlay. this._marginDomNode.style.zIndex = '10'; this._diffActions = document.createElement('div'); this._diffActions.className = Codicon.lightBulb.classNames + ' lightbulb-glyph'; this._diffActions.style.position = 'absolute'; const lineHeight = editor.getOption(58 /* lineHeight */); const lineFeed = editor.getModel().getEOL(); this._diffActions.style.right = '0px'; this._diffActions.style.visibility = 'hidden'; this._diffActions.style.height = `${lineHeight}px`; this._diffActions.style.lineHeight = `${lineHeight}px`; this._marginDomNode.appendChild(this._diffActions); const actions = []; const isDeletion = diff.modifiedEndLineNumber === 0; // default action actions.push(new Action('diff.clipboard.copyDeletedContent', isDeletion ? (diff.originalEndLineNumber > diff.modifiedStartLineNumber ? nls.localize('diff.clipboard.copyDeletedLinesContent.label', "Copy deleted lines") : nls.localize('diff.clipboard.copyDeletedLinesContent.single.label', "Copy deleted line")) : (diff.originalEndLineNumber > diff.modifiedStartLineNumber ? nls.localize('diff.clipboard.copyChangedLinesContent.label', "Copy changed lines") : nls.localize('diff.clipboard.copyChangedLinesContent.single.label', "Copy changed line")), undefined, true, () => __awaiter(this, void 0, void 0, function* () { const range = new Range(diff.originalStartLineNumber, 1, diff.originalEndLineNumber + 1, 1); const deletedText = diff.originalModel.getValueInRange(range); yield this._clipboardService.writeText(deletedText); }))); let currentLineNumberOffset = 0; let copyLineAction = undefined; if (diff.originalEndLineNumber > diff.modifiedStartLineNumber) { copyLineAction = new Action('diff.clipboard.copyDeletedLineContent', isDeletion ? nls.localize('diff.clipboard.copyDeletedLineContent.label', "Copy deleted line ({0})", diff.originalStartLineNumber) : nls.localize('diff.clipboard.copyChangedLineContent.label', "Copy changed line ({0})", diff.originalStartLineNumber), undefined, true, () => __awaiter(this, void 0, void 0, function* () { const lineContent = diff.originalModel.getLineContent(diff.originalStartLineNumber + currentLineNumberOffset); if (lineContent === '') { // empty line const eof = diff.originalModel.getEndOfLineSequence(); yield this._clipboardService.writeText(eof === 0 /* LF */ ? '\n' : '\r\n'); } else { yield this._clipboardService.writeText(lineContent); } })); actions.push(copyLineAction); } const readOnly = editor.getOption(80 /* readOnly */); if (!readOnly) { actions.push(new Action('diff.inline.revertChange', nls.localize('diff.inline.revertChange.label', "Revert this change"), undefined, true, () => __awaiter(this, void 0, void 0, function* () { const range = new Range(diff.originalStartLineNumber, 1, diff.originalEndLineNumber, diff.originalModel.getLineMaxColumn(diff.originalEndLineNumber)); const deletedText = diff.originalModel.getValueInRange(range); if (diff.modifiedEndLineNumber === 0) { // deletion only const column = editor.getModel().getLineMaxColumn(diff.modifiedStartLineNumber); editor.executeEdits('diffEditor', [ { range: new Range(diff.modifiedStartLineNumber, column, diff.modifiedStartLineNumber, column), text: lineFeed + deletedText } ]); } else { const column = editor.getModel().getLineMaxColumn(diff.modifiedEndLineNumber); editor.executeEdits('diffEditor', [ { range: new Range(diff.modifiedStartLineNumber, 1, diff.modifiedEndLineNumber, column), text: deletedText } ]); } }))); } const showContextMenu = (x, y) => { this._contextMenuService.showContextMenu({ getAnchor: () => { return { x, y }; }, getActions: () => { if (copyLineAction) { copyLineAction.label = isDeletion ? nls.localize('diff.clipboard.copyDeletedLineContent.label', "Copy deleted line ({0})", diff.originalStartLineNumber + currentLineNumberOffset) : nls.localize('diff.clipboard.copyChangedLineContent.label', "Copy changed line ({0})", diff.originalStartLineNumber + currentLineNumberOffset); } return actions; }, autoSelectFirstItem: true }); }; this._register(dom.addStandardDisposableListener(this._diffActions, 'mousedown', e => { const { top, height } = dom.getDomNodePagePosition(this._diffActions); let pad = Math.floor(lineHeight / 3); e.preventDefault(); showContextMenu(e.posx, top + height + pad); })); this._register(editor.onMouseMove((e) => { if (e.target.type === 8 /* CONTENT_VIEW_ZONE */ || e.target.type === 5 /* GUTTER_VIEW_ZONE */) { const viewZoneId = e.target.detail.viewZoneId; if (viewZoneId === this._viewZoneId) { this.visibility = true; currentLineNumberOffset = this._updateLightBulbPosition(this._marginDomNode, e.event.browserEvent.y, lineHeight); } else { this.visibility = false; } } else { this.visibility = false; } })); this._register(editor.onMouseDown((e) => { if (!e.event.rightButton) { return; } if (e.target.type === 8 /* CONTENT_VIEW_ZONE */ || e.target.type === 5 /* GUTTER_VIEW_ZONE */) { const viewZoneId = e.target.detail.viewZoneId; if (viewZoneId === this._viewZoneId) { e.event.preventDefault(); currentLineNumberOffset = this._updateLightBulbPosition(this._marginDomNode, e.event.browserEvent.y, lineHeight); showContextMenu(e.event.posx, e.event.posy + lineHeight); } } })); } get visibility() { return this._visibility; } set visibility(_visibility) { if (this._visibility !== _visibility) { this._visibility = _visibility; if (_visibility) { this._diffActions.style.visibility = 'visible'; } else { this._diffActions.style.visibility = 'hidden'; } } } _updateLightBulbPosition(marginDomNode, y, lineHeight) { const { top } = dom.getDomNodePagePosition(marginDomNode); const offset = y - top; const lineNumberOffset = Math.floor(offset / lineHeight); const newTop = lineNumberOffset * lineHeight; this._diffActions.style.top = `${newTop}px`; if (this.diff.viewLineCounts) { let acc = 0; for (let i = 0; i < this.diff.viewLineCounts.length; i++) { acc += this.diff.viewLineCounts[i]; if (lineNumberOffset < acc) { return i; } } } return lineNumberOffset; } }