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

dynamic height review widget

上级 b638f1ad
......@@ -141,7 +141,8 @@ export class CodeWindow implements ICodeWindow {
title: product.nameLong,
webPreferences: {
'backgroundThrottling': false, // by default if Code is in the background, intervals and timeouts get throttled,
disableBlinkFeatures: 'Auxclick' // disable auxclick events (see https://developers.google.com/web/updates/2016/10/auxclick)
disableBlinkFeatures: 'Auxclick', // disable auxclick events (see https://developers.google.com/web/updates/2016/10/auxclick)
experimentalFeatures: true
}
};
......
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 15 15" style="enable-background:new 0 0 15 15;">
<line style="fill:none;stroke:#C5C5C5;stroke-miterlimit:10" x1="10" y1="7.5" x2="5" y2="7.5"/>
<line style="fill:none;stroke:#C5C5C5;stroke-miterlimit:10" x1="7.5" y1="5" x2="7.5" y2="10"/>
</svg>
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-editor .margin-view-overlays .review {
background-image: url('comment.svg');
cursor: pointer;
}
.monaco-editor .review-widget {
width: 100%;
position: absolute;
margin: 0px 20px 20px 20px;
}
.monaco-editor .review-widget pre {
overflow: auto;
word-wrap: normal;
white-space: pre;
}
.monaco-editor.vs-dark .review-widget .author {
color: #fff;
}
.monaco-editor.vs-dark .review-widget span.created_at {
color: #e0e0e0;
}
.monaco-editor .review-widget p,
.monaco-editor .review-widget ul {
margin: 8px 0;
}
.monaco-editor .review-widget p:first-child,
.monaco-editor .review-widget ul:first-child {
margin-top: 0;
}
.monaco-editor .review-widget p:last-child,
.monaco-editor .review-widget ul:last-child {
margin-bottom: 0;
}
.monaco-editor .review-widget ul {
padding-left: 20px;
}
.monaco-editor .review-widget li > p {
margin-bottom: 0;
}
.monaco-editor .review-widget li > ul {
margin-top: 0;
}
.monaco-editor .review-widget code {
border-radius: 3px;
padding: 0 0.4em;
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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!./review';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType, IViewZone } from 'vs/editor/browser/editorBrowser';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IMarginData } from 'vs/editor/browser/controller/mouseTarget';
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { TrackedRangeStickiness } from 'vs/editor/common/model';
import { ZoneWidget, IOptions } from '../zoneWidget/zoneWidget';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer';
import { IComment, getComments } from 'vs/editor/contrib/review/reviewProvider';
import { RawContextKey, IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget';
export const ctxReviewPanelVisible = new RawContextKey<boolean>('reviewPanelVisible', false);
export const ID = 'editor.contrib.review';
declare var ResizeObserver: any;
const REVIEWL_DECORATION = ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
linesDecorationsClassName: 'review'
});
export class ReviewViewZone implements IViewZone {
public readonly afterLineNumber: number;
public readonly domNode: HTMLElement;
private callback: (top: number) => void;
constructor(afterLineNumber: number, onDomNodeTop: (top: number) => void) {
this.afterLineNumber = afterLineNumber;
this.callback = onDomNodeTop;
this.domNode = document.createElement('div');
this.domNode.className = 'review-viewzone';
}
onDomNodeTop(top: number): void {
this.callback(top);
}
}
export class ReviewZoneWidget extends ZoneWidget {
private _domNode: HTMLElement;
private _resizeObserver: any;
constructor(editor: ICodeEditor, options: IOptions = {}) {
super(editor, options);
this._resizeObserver = null;
this.create();
}
protected _fillContainer(container: HTMLElement): void {
this._domNode = document.createElement('div');
this._domNode.className = 'review-widget';
container.appendChild(this._domNode);
}
display(comments: IComment[], lineNumber: number) {
this.show({ lineNumber: lineNumber, column: 1 }, 2);
for (let i = 0; i < comments.length; i++) {
let singleCommentContainer = document.createElement('div');
singleCommentContainer.className = 'review-comment-contents';
let header = document.createElement('h4');
let author = document.createElement('strong');
author.className = 'author';
author.innerText = comments[i].user;
let time = document.createElement('span');
time.className = 'created_at';
time.innerText = comments[i].created_at;
header.appendChild(author);
header.appendChild(time);
singleCommentContainer.appendChild(header);
let body = document.createElement('div');
body.className = 'comment-body';
singleCommentContainer.appendChild(body);
let md = new MarkdownString(comments[i].body);
body.appendChild(renderMarkdown(md));
this._domNode.appendChild(singleCommentContainer);
// this._domNode.appendChild(document.createElement('textarea'));
}
this._resizeObserver = new ResizeObserver(entries => {
if (entries[0].target === this._domNode) {
const lineHeight = this.editor.getConfiguration().lineHeight;
const arrowHeight = Math.round(lineHeight / 3);
const computedLinesNumber = Math.ceil((entries[0].contentRect.height + arrowHeight + 30) / lineHeight);
this._relayout(computedLinesNumber);
}
});
this._resizeObserver.observe(this._domNode);
}
dispose() {
super.dispose();
this._resizeObserver.disconnect();
this._resizeObserver = null;
}
}
export class ReviewController implements IEditorContribution {
private globalToDispose: IDisposable[];
private localToDispose: IDisposable[];
private editor: ICodeEditor;
private decorationIDs: string[];
private _domNode: HTMLElement;
private _zoneWidget: ReviewZoneWidget;
private _reviewPanelVisible: IContextKey<boolean>;
constructor(
editor: ICodeEditor,
@IContextKeyService contextKeyService: IContextKeyService,
) {
this.editor = editor;
this.globalToDispose = [];
this.localToDispose = [];
this.decorationIDs = [];
this.mouseDownInfo = null;
this._reviewPanelVisible = ctxReviewPanelVisible.bindTo(contextKeyService);
this._domNode = document.createElement('div');
this._domNode.className = 'review-widget';
this._zoneWidget = null;
this.globalToDispose.push(this.editor.onDidChangeModel(() => this.onModelChanged()));
}
public static get(editor: ICodeEditor): ReviewController {
return editor.getContribution<ReviewController>(ID);
}
getId(): string {
return ID;
}
dispose(): void {
this.globalToDispose = dispose(this.globalToDispose);
this.localToDispose = dispose(this.localToDispose);
if (this._zoneWidget) {
this._zoneWidget.dispose();
this._zoneWidget = null;
}
this.editor = null;
}
public onModelChanged(): void {
this.localToDispose = dispose(this.localToDispose);
this.localToDispose.push(this.editor.onMouseDown(e => this.onEditorMouseDown(e)));
this.localToDispose.push(this.editor.onMouseUp(e => this.onEditorMouseUp(e)));
this.editor.changeDecorations(accessor => {
this.decorationIDs = accessor.deltaDecorations(this.decorationIDs, [
{
range: {
startLineNumber: 6,
startColumn: 1,
endLineNumber: 6,
endColumn: 1
},
options: REVIEWL_DECORATION
}
]);
});
}
private mouseDownInfo: { lineNumber: number, iconClicked: boolean };
private onEditorMouseDown(e: IEditorMouseEvent): void {
if (!e.event.leftButton) {
return;
}
let range = e.target.range;
if (!range) {
return;
}
let iconClicked = false;
switch (e.target.type) {
case MouseTargetType.GUTTER_LINE_DECORATIONS:
const data = e.target.detail as IMarginData;
const gutterOffsetX = data.offsetX - data.glyphMarginWidth - data.lineNumbersWidth;
if (gutterOffsetX <= 10) {
return;
}
iconClicked = true;
break;
default:
return;
}
this.mouseDownInfo = { lineNumber: range.startLineNumber, iconClicked };
}
private onEditorMouseUp(e: IEditorMouseEvent): void {
if (!this.mouseDownInfo) {
return;
}
let lineNumber = this.mouseDownInfo.lineNumber;
let iconClicked = this.mouseDownInfo.iconClicked;
let range = e.target.range;
if (!range || range.startLineNumber !== lineNumber) {
return;
}
if (iconClicked) {
if (e.target.type !== MouseTargetType.GUTTER_LINE_DECORATIONS) {
return;
}
}
if (this._zoneWidget && this._zoneWidget.position.lineNumber === lineNumber) {
return;
}
this._reviewPanelVisible.set(true);
this._zoneWidget = new ReviewZoneWidget(this.editor);
this._zoneWidget.display(getComments(), lineNumber);
}
public closeWidget(): void {
this._reviewPanelVisible.reset();
if (this._zoneWidget) {
this._zoneWidget.dispose();
this._zoneWidget = null;
}
this.editor.focus();
}
}
registerEditorContribution(ReviewController);
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'closeReviewPanel',
weight: KeybindingsRegistry.WEIGHT.editorContrib(),
primary: KeyCode.Escape,
secondary: [KeyMod.Shift | KeyCode.Escape],
when: ctxReviewPanelVisible,
handler: closeReviewPanel
});
export function getOuterEditor(accessor: ServicesAccessor): ICodeEditor {
let editor = accessor.get(ICodeEditorService).getFocusedCodeEditor();
if (editor instanceof EmbeddedCodeEditorWidget) {
return editor.getParentEditor();
}
return editor;
}
function closeReviewPanel(accessor: ServicesAccessor, args: any) {
var outerEditor = getOuterEditor(accessor);
if (!outerEditor) {
return;
}
let controller = ReviewController.get(outerEditor);
if (!controller) {
return;
}
controller.closeWidget();
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
export interface IComment {
user: string;
body: string;
created_at: string;
updated_at: string;
html_url: string;
pull_request_url: string;
}
export function getComments(): IComment[] {
return [
{
user: 'isidorn',
body: `The proper way to do all this action command business is the following:\r\n* Have a command that is self contained and does the actual work\r\n* Use the command everywhere except Action Bar\r\n* For the Action Bar instatiate an Action that will only use the commandService to execute the command\r\n\r\nThat patter is cleanest and we use it all over the place (especially explorer).\r\nIf you do not want to tackle it in this PR then you can create a debt item.`,
created_at: '2018-03-22T09:13:21Z',
updated_at: '2018-03-26T18:27:15Z',
html_url: 'https://github.com/Microsoft/vscode/pull/46311#discussion_r176348736',
pull_request_url: 'https://api.github.com/repos/Microsoft/vscode/pulls/46311'
},
{
user: 'roblourens',
body: `This works fine and we have it for instance in debug land.\r\nHowever this is an old way to do this - it is a sort of bridge between comands and actions.\r\n\r\nSince all your things could be commands this can than be much simpliar without any action items and things like that.\r\nI suggest to look here for an inspiration on how to do it if all you guys are commands\r\nhttps://github.com/Microsoft/vscode/blob/roblou/searchContextMenu/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts#L312`,
created_at: '2018-03-22T09:20:05Z',
updated_at: '2018-03-26T18:27:15Z',
html_url: 'https://github.com/Microsoft/vscode/pull/46311#discussion_r176350585',
pull_request_url: 'https://api.github.com/repos/Microsoft/vscode/pulls/46311',
}
];
}
\ No newline at end of file
......@@ -22,6 +22,7 @@ import 'vs/editor/contrib/cursorUndo/cursorUndo';
import 'vs/editor/contrib/dnd/dnd';
import 'vs/editor/contrib/find/findController';
import 'vs/editor/contrib/folding/folding';
import 'vs/editor/contrib/review/review';
import 'vs/editor/contrib/format/formatActions';
import 'vs/editor/contrib/goToDeclaration/goToDeclarationCommands';
import 'vs/editor/contrib/goToDeclaration/goToDeclarationMouse';
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册