提交 c9042cb5 编写于 作者: P Peng Lyu

Hover actions on gutter in inline diff editor.

上级 93ca4df1
......@@ -40,6 +40,9 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
import { INotificationService } from 'vs/platform/notification/common/notification';
import { defaultInsertColor, defaultRemoveColor, diffBorder, diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, scrollbarShadow } from 'vs/platform/theme/common/colorRegistry';
import { ITheme, IThemeService, getThemeTypeSelector, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IDiffLinesChange, InlineDiffMargin } from 'vs/editor/browser/widget/inlineDiffMargin';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
interface IEditorDiffDecorations {
decorations: IModelDeltaDecoration[];
......@@ -47,7 +50,7 @@ interface IEditorDiffDecorations {
}
interface IEditorDiffDecorationsWithZones extends IEditorDiffDecorations {
zones: editorBrowser.IViewZone[];
zones: IMyViewZone[];
}
interface IEditorsDiffDecorationsWithZones {
......@@ -56,8 +59,8 @@ interface IEditorsDiffDecorationsWithZones {
}
interface IEditorsZones {
original: editorBrowser.IViewZone[];
modified: editorBrowser.IViewZone[];
original: IMyViewZone[];
modified: IMyViewZone[];
}
interface IDiffEditorWidgetStyle {
......@@ -70,11 +73,16 @@ interface IDiffEditorWidgetStyle {
class VisualEditorState {
private _zones: string[];
private inlineDiffMargins: InlineDiffMargin[];
private _zonesMap: { [zoneId: string]: boolean; };
private _decorations: string[];
constructor() {
constructor(
private _contextMenuService: IContextMenuService,
private _clipboardService: IClipboardService
) {
this._zones = [];
this.inlineDiffMargins = [];
this._zonesMap = {};
this._decorations = [];
}
......@@ -108,13 +116,22 @@ class VisualEditorState {
for (let i = 0, length = this._zones.length; i < length; i++) {
viewChangeAccessor.removeZone(this._zones[i]);
}
for (let i = 0, length = this.inlineDiffMargins.length; i < length; i++) {
this.inlineDiffMargins[i].dispose();
}
this._zones = [];
this._zonesMap = {};
this.inlineDiffMargins = [];
for (let i = 0, length = newDecorations.zones.length; i < length; i++) {
newDecorations.zones[i].suppressMouseDown = true;
let zoneId = viewChangeAccessor.addZone(newDecorations.zones[i]);
const viewZone = <editorBrowser.IViewZone>newDecorations.zones[i];
viewZone.suppressMouseDown = false;
let zoneId = viewChangeAccessor.addZone(viewZone);
this._zones.push(zoneId);
this._zonesMap[String(zoneId)] = true;
if (newDecorations.zones[i].diff && viewZone.marginDomNode) {
this.inlineDiffMargins.push(new InlineDiffMargin(viewZone.marginDomNode, editor, newDecorations.zones[i].diff!, this._contextMenuService, this._clipboardService));
}
}
});
......@@ -202,7 +219,9 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
@IInstantiationService instantiationService: IInstantiationService,
@ICodeEditorService codeEditorService: ICodeEditorService,
@IThemeService themeService: IThemeService,
@INotificationService notificationService: INotificationService
@INotificationService notificationService: INotificationService,
@IContextMenuService contextMenuService: IContextMenuService,
@IClipboardService clipboardService: IClipboardService
) {
super();
......@@ -282,8 +301,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this._currentlyChangingViewZones = false;
this._diffComputationToken = 0;
this._originalEditorState = new VisualEditorState();
this._modifiedEditorState = new VisualEditorState();
this._originalEditorState = new VisualEditorState(contextMenuService, clipboardService);
this._modifiedEditorState = new VisualEditorState(contextMenuService, clipboardService);
this._isVisible = true;
this._isHandlingScrollEvent = false;
......@@ -1258,6 +1277,7 @@ interface IMyViewZone {
minWidthInPx?: number;
domNode: HTMLElement | null;
marginDomNode?: HTMLElement | null;
diff?: IDiffLinesChange;
}
class ForeignViewZonesIterator {
......@@ -1469,12 +1489,12 @@ abstract class ViewZonesComputer {
};
}
private static _ensureDomNodes(zones: IMyViewZone[]): editorBrowser.IViewZone[] {
private static _ensureDomNodes(zones: IMyViewZone[]): IMyViewZone[] {
return zones.map((z) => {
if (!z.domNode) {
z.domNode = createFakeLinesDiv();
}
return <editorBrowser.IViewZone>z;
return z;
});
}
......@@ -1977,8 +1997,10 @@ class InlineViewZonesComputer extends ViewZonesComputer {
let lineHeight = this.modifiedEditorConfiguration.lineHeight;
const typicalHalfwidthCharacterWidth = this.modifiedEditorConfiguration.fontInfo.typicalHalfwidthCharacterWidth;
let maxCharsPerLine = 0;
const originalContent: string[] = [];
for (let lineNumber = lineChange.originalStartLineNumber; lineNumber <= lineChange.originalEndLineNumber; lineNumber++) {
maxCharsPerLine = Math.max(maxCharsPerLine, this._renderOriginalLine(lineNumber - lineChange.originalStartLineNumber, this.originalModel, this.modifiedEditorConfiguration, this.modifiedEditorTabSize, lineNumber, decorations, sb));
originalContent.push(this.originalModel.getLineContent(lineNumber));
if (this.renderIndicators) {
let index = lineNumber - lineChange.originalStartLineNumber;
......@@ -2005,7 +2027,14 @@ class InlineViewZonesComputer extends ViewZonesComputer {
heightInLines: lineChangeOriginalLength,
minWidthInPx: (maxCharsPerLine * typicalHalfwidthCharacterWidth),
domNode: domNode,
marginDomNode: marginDomNode
marginDomNode: marginDomNode,
diff: {
originalStartLineNumber: lineChange.originalStartLineNumber,
originalEndLineNumber: lineChange.originalEndLineNumber,
modifiedStartLineNumber: lineChange.modifiedStartLineNumber,
modifiedEndLineNumber: lineChange.modifiedEndLineNumber,
originalContent: originalContent
}
};
}
......
......@@ -16,6 +16,8 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
export class EmbeddedCodeEditorWidget extends CodeEditorWidget {
......@@ -74,9 +76,11 @@ export class EmbeddedDiffEditorWidget extends DiffEditorWidget {
@IInstantiationService instantiationService: IInstantiationService,
@ICodeEditorService codeEditorService: ICodeEditorService,
@IThemeService themeService: IThemeService,
@INotificationService notificationService: INotificationService
@INotificationService notificationService: INotificationService,
@IContextMenuService contextMenuService: IContextMenuService,
@IClipboardService clipboardService: IClipboardService
) {
super(domElement, parentEditor.getRawConfiguration(), editorWorkerService, contextKeyService, instantiationService, codeEditorService, themeService, notificationService);
super(domElement, parentEditor.getRawConfiguration(), editorWorkerService, contextKeyService, instantiationService, codeEditorService, themeService, notificationService, contextMenuService, clipboardService);
this._parentEditor = parentEditor;
this._overwriteOptions = options;
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
import { Action } from 'vs/base/common/actions';
import { Disposable } from 'vs/base/common/lifecycle';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { Range } from 'vs/editor/common/core/range';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
export interface IDiffLinesChange {
readonly originalStartLineNumber: number;
readonly originalEndLineNumber: number;
readonly modifiedStartLineNumber: number;
readonly modifiedEndLineNumber: number;
readonly originalContent: string[];
}
export class InlineDiffMargin extends Disposable {
private readonly _lightBulb: HTMLElement;
constructor(
marginDomNode: HTMLElement,
public editor: CodeEditorWidget,
public diff: IDiffLinesChange,
private _contextMenuService: IContextMenuService,
private _clipboardService: IClipboardService
) {
super();
// make sure the diff margin shows above overlay.
marginDomNode.style.zIndex = '10';
this._lightBulb = document.createElement('div');
this._lightBulb.className = 'lightbulb-glyph';
this._lightBulb.style.position = 'absolute';
const lineHeight = editor.getConfiguration().lineHeight;
const lineFeed = editor.getModel()!.getEOL();
this._lightBulb.style.right = '0px';
this._lightBulb.style.visibility = 'hidden';
this._lightBulb.style.height = `${lineHeight}px`;
marginDomNode.appendChild(this._lightBulb);
const actions = [
new Action(
'diff.clipboard.copyDeletedContent',
nls.localize('diff.clipboard.copyDeletedContent.label', "Copy deleted lines content to clipboard"),
undefined,
true,
async () => {
await this._clipboardService.writeText(diff.originalContent.join(lineFeed) + lineFeed);
}
)
];
let currentLineNumberOffset = 0;
const copyLineAction = new Action(
'diff.clipboard.copyDeletedLineContent',
nls.localize('diff.clipboard.copyDeletedLineContent.label', "Copy deleted line {0} content to clipboard", diff.originalStartLineNumber),
undefined,
true,
async () => {
await this._clipboardService.writeText(diff.originalContent[currentLineNumberOffset] + lineFeed);
}
);
actions.push(copyLineAction);
const readOnly = editor.getConfiguration().readOnly;
if (!readOnly) {
actions.push(new Action('diff.inline.revertChange', nls.localize('diff.inline.revertChange.label', "Revert this change"), undefined, true, async () => {
editor.executeEdits('diffEditor', [
{
range: new Range(diff.modifiedStartLineNumber, 1, diff.modifiedEndLineNumber + 1, 1),
text: diff.originalContent.join(lineFeed) + lineFeed
}
]);
}));
}
this._register(dom.addStandardDisposableListener(marginDomNode, 'mouseenter', e => {
this._lightBulb.style.visibility = 'visible';
currentLineNumberOffset = this._updateLightBulbPosition(marginDomNode, e.y, lineHeight);
}));
this._register(dom.addStandardDisposableListener(marginDomNode, 'mouseleave', e => {
this._lightBulb.style.visibility = 'hidden';
}));
this._register(dom.addStandardDisposableListener(marginDomNode, 'mousemove', e => {
currentLineNumberOffset = this._updateLightBulbPosition(marginDomNode, e.y, lineHeight);
}));
this._register(dom.addStandardDisposableListener(this._lightBulb, 'mousedown', e => {
const { top, height } = dom.getDomNodePagePosition(this._lightBulb);
let pad = Math.floor(lineHeight / 3) + lineHeight;
this._contextMenuService.showContextMenu({
getAnchor: () => {
return {
x: e.posx,
y: top + height + pad
};
},
getActions: () => {
copyLineAction.label = nls.localize('diff.clipboard.copyDeletedLineContent.label', "Copy deleted line {0} content to clipboard", diff.originalStartLineNumber + currentLineNumberOffset);
return actions;
},
autoSelectFirstItem: true
});
}));
}
private _updateLightBulbPosition(marginDomNode: HTMLElement, y: number, lineHeight: number): number {
const { top } = dom.getDomNodePagePosition(marginDomNode);
const offset = y - top;
const lineNumberOffset = Math.floor(offset / lineHeight);
const newTop = lineNumberOffset * lineHeight;
this._lightBulb.style.top = `${newTop}px`;
return lineNumberOffset;
}
}
......@@ -21,7 +21,7 @@ import { IMenuItem, MenuId, MenuRegistry } from 'vs/platform/actions/common/acti
import { CommandsRegistry, ICommandHandler, ICommandService } from 'vs/platform/commands/common/commands';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ContextKeyExpr, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
......@@ -29,6 +29,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
import { StandaloneCodeEditorNLS } from 'vs/editor/common/standaloneStrings';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
/**
* Description of an action contribution
......@@ -373,7 +374,9 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon
@ICodeEditorService codeEditorService: ICodeEditorService,
@IStandaloneThemeService themeService: IStandaloneThemeService,
@INotificationService notificationService: INotificationService,
@IConfigurationService configurationService: IConfigurationService
@IConfigurationService configurationService: IConfigurationService,
@IContextMenuService contextMenuService: IContextMenuService,
@IClipboardService clipboardService: IClipboardService
) {
applyConfigurationValues(configurationService, options, true);
options = options || {};
......@@ -381,7 +384,7 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon
options.theme = themeService.setTheme(options.theme);
}
super(domElement, options, editorWorkerService, contextKeyService, instantiationService, codeEditorService, themeService, notificationService);
super(domElement, options, editorWorkerService, contextKeyService, instantiationService, codeEditorService, themeService, notificationService, contextMenuService, clipboardService);
this._contextViewService = <ContextViewService>contextViewService;
this._configurationService = configurationService;
......
......@@ -30,7 +30,7 @@ import { IStandaloneThemeData, IStandaloneThemeService } from 'vs/editor/standal
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IMarker, IMarkerData } from 'vs/platform/markers/common/markers';
......@@ -38,6 +38,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
import { clearAllFontInfos } from 'vs/editor/browser/config/configuration';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
......@@ -119,6 +120,8 @@ export function createDiffEditor(domElement: HTMLElement, options?: IDiffEditorC
services.get(IStandaloneThemeService),
services.get(INotificationService),
services.get(IConfigurationService),
services.get(IContextMenuService),
services.get(IClipboardService)
);
});
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册