提交 105ba504 编写于 作者: P Peng Lyu

gravatar, title bar for comment thread, show comment titles when file opened.

上级 88ac1012
......@@ -14,7 +14,9 @@ export interface DiffHunkRange {
export interface User {
id: string;
login: string;
avatar_url: string;
}
export interface Comment {
url: string;
id: string;
......
......@@ -135,11 +135,24 @@ export class PRProvider implements vscode.TreeDataProvider<PRGroup | PullRequest
let promises = this.repository.remotes.map(async remote => {
const octo = await this.crendentialStore.getOctokit(remote);
if (octo) {
const { data } = await octo.pullRequests.getAll({
let { data } = await octo.pullRequests.getAll({
owner: remote.owner,
repo: remote.name
repo: remote.name,
per_page: 100
});
return data.map(item => new PullRequest(octo, remote, item));
let ret = data.map(item => new PullRequest(octo, remote, item));
if (ret.length >= 100) {
let secondPage = await octo.pullRequests.getAll({
owner: remote.owner,
repo: remote.name,
per_page: 100,
page: 2
});
ret.push(...secondPage.data.map(item => new PullRequest(octo, remote, item)));
}
return ret;
}
});
......@@ -204,7 +217,8 @@ export class PRProvider implements vscode.TreeDataProvider<PRGroup | PullRequest
comments: comments.map(comment => {
return {
body: new vscode.MarkdownString(comment.body),
userName: comment.user.login
userName: comment.user.login,
gravatar: comment.user.avatar_url
};
})
});
......
......@@ -946,6 +946,7 @@ export interface CommentThread {
export interface Comment {
readonly body: IMarkdownString;
readonly userName: string;
readonly gravatar: string;
}
export interface CommentProvider {
......
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="3 3 16 16" enable-background="new 3 3 16 16"><polygon fill="#e8e8e8" points="12.597,11.042 15.4,13.845 13.844,15.4 11.042,12.598 8.239,15.4 6.683,13.845 9.485,11.042 6.683,8.239 8.238,6.683 11.042,9.486 13.845,6.683 15.4,8.239"/></svg>
\ No newline at end of file
......@@ -13,50 +13,143 @@
.monaco-editor .review-widget {
width: 100%;
position: absolute;
margin: 0px 20px 20px 20px;
}
.monaco-editor .review-widget pre {
.monaco-editor .review-widget .body .review-comment {
margin-left: 20px;
padding: 8px 16px 8px 0px;
}
.monaco-editor .review-widget .body .review-comment .float-left {
float: left;
margin-top: 4px !important;
}
.monaco-editor .review-widget .body .review-comment .float-left img.avatar {
height: 28px;
width: 28px;
display: inline-block;
overflow: hidden;
line-height: 1;
vertical-align: middle;
border-radius: 3px;
border-style: none;
}
.monaco-editor .review-widget .body .review-comment .review-comment-contents {
margin-left: 44px;
}
.monaco-editor .review-widget .body pre {
overflow: auto;
word-wrap: normal;
white-space: pre;
}
.monaco-editor.vs-dark .review-widget .author {
.monaco-editor.vs-dark .review-widget .body .review-comment .review-comment-contents h4 {
margin: 0;
}
.monaco-editor.vs-dark .review-widget .body .review-comment .review-comment-contents .author {
color: #fff;
font-weight: 600;
}
.monaco-editor.vs-dark .review-widget .body .review-comment .review-comment-contents .comment-body {
padding-top: 4px;
}
.monaco-editor.vs-dark .review-widget span.created_at {
.monaco-editor.vs-dark .review-widget .body span.created_at {
color: #e0e0e0;
}
.monaco-editor .review-widget p,
.monaco-editor .review-widget ul {
.monaco-editor .review-widget .body p,
.monaco-editor .review-widget .body ul {
margin: 8px 0;
}
.monaco-editor .review-widget p:first-child,
.monaco-editor .review-widget ul:first-child {
.monaco-editor .review-widget .body p:first-child,
.monaco-editor .review-widget .body ul:first-child {
margin-top: 0;
}
.monaco-editor .review-widget p:last-child,
.monaco-editor .review-widget ul:last-child {
.monaco-editor .review-widget .body p:last-child,
.monaco-editor .review-widget .body ul:last-child {
margin-bottom: 0;
}
.monaco-editor .review-widget ul {
.monaco-editor .review-widget .body ul {
padding-left: 20px;
}
.monaco-editor .review-widget li > p {
.monaco-editor .review-widget .body li > p {
margin-bottom: 0;
}
.monaco-editor .review-widget li > ul {
.monaco-editor .review-widget .body li > ul {
margin-top: 0;
}
.monaco-editor .review-widget code {
.monaco-editor .review-widget .body code {
border-radius: 3px;
padding: 0 0.4em;
}
\ No newline at end of file
}
.monaco-editor .review-widget .head {
-webkit-box-sizing: border-box;
-o-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
display: flex;
}
.monaco-editor .review-widget .head .review-title {
display: inline-block;
font-size: 13px;
margin-left: 20px;
cursor: pointer;
}
.monaco-editor .review-widget .head .review-title .dirname:not(:empty) {
font-size: 0.9em;
margin-left: 0.5em;
}
.monaco-editor .review-widget .head .review-actions {
flex: 1;
text-align: right;
padding-right: 2px;
}
.monaco-editor .review-widget .head .review-actions > .monaco-action-bar {
display: inline-block;
}
.monaco-editor .review-widget .head .review-actions > .monaco-action-bar,
.monaco-editor .review-widget .head .review-actions > .monaco-action-bar > .actions-container {
height: 100%;
}
.monaco-editor .review-widget .head .review-actions > .monaco-action-bar .action-item {
margin-left: 4px;
}
.monaco-editor .review-widget .head .review-actions > .monaco-action-bar .action-label {
width: 16px;
height: 100%;
margin: 0;
line-height: inherit;
background-repeat: no-repeat;
background-position: center center;
}
.monaco-editor .review-widget .head .review-actions > .monaco-action-bar .action-label.octicon {
margin: 0;
}
.monaco-editor .review-widget .head .review-actions .action-label.icon.close-review-action {
background: url('close.svg') center center no-repeat;
}
.monaco-editor .review-widget > .body {
border-top: 1px solid;
position: relative;
}
......@@ -5,9 +5,11 @@
'use strict';
import 'vs/css!./review';
import * as nls from 'vs/nls';
import * as modes from 'vs/editor/common/modes';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType, IViewZone } from 'vs/editor/browser/editorBrowser';
import { $ } from 'vs/base/browser/builder';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
......@@ -20,6 +22,11 @@ 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';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { Action } from 'vs/base/common/actions';
import { registerThemingParticipant, ITheme, IThemeService } from 'vs/platform/theme/common/themeService';
import { peekViewEditorBackground, peekViewBorder, } from 'vs/editor/contrib/referenceSearch/referencesWidget';
import { Color } from 'vs/base/common/color';
export const ctxReviewPanelVisible = new RawContextKey<boolean>('reviewPanelVisible', false);
export const ID = 'editor.contrib.review';
......@@ -28,7 +35,7 @@ declare var ResizeObserver: any;
const REVIEWL_DECORATION = ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
linesDecorationsClassName: 'review'
glyphMarginClassName: 'review'
});
export class ReviewViewZone implements IViewZone {
......@@ -50,27 +57,85 @@ export class ReviewViewZone implements IViewZone {
}
export class ReviewZoneWidget extends ZoneWidget {
private _domNode: HTMLElement;
private _headElement: HTMLElement;
protected _primaryHeading: HTMLElement;
protected _secondaryHeading: HTMLElement;
protected _metaHeading: HTMLElement;
protected _actionbarWidget: ActionBar;
private _bodyElement: HTMLElement;
private _resizeObserver: any;
private _comments: modes.Comment[];
constructor(editor: ICodeEditor, options: IOptions = {}) {
constructor(@IThemeService private themeService: IThemeService,
editor: ICodeEditor, options: IOptions = {}, comments: modes.Comment[]) {
super(editor, options);
this._resizeObserver = null;
this._comments = comments;
this.create();
this.themeService.onThemeChange(this._applyTheme, this);
}
protected _fillContainer(container: HTMLElement): void {
this._domNode = document.createElement('div');
this._domNode.className = 'review-widget';
container.appendChild(this._domNode);
this.setCssClass('review-widget');
this._headElement = <HTMLDivElement>$('.head').getHTMLElement();
container.appendChild(this._headElement);
this._fillHead(this._headElement);
this._bodyElement = <HTMLDivElement>$('.body').getHTMLElement(); document.createElement('div');
container.appendChild(this._bodyElement);
}
protected _fillHead(container: HTMLElement): void {
var titleElement = $('.review-title').
// on(dom.EventType.CLICK, e => this._onTitleClick(<MouseEvent>e)).
appendTo(this._headElement).
getHTMLElement();
this._primaryHeading = $('span.filename').appendTo(titleElement).getHTMLElement();
this._secondaryHeading = $('span.dirname').appendTo(titleElement).getHTMLElement();
this._metaHeading = $('span.meta').appendTo(titleElement).getHTMLElement();
let primaryHeading = 'Discussion';
$(this._primaryHeading).safeInnerHtml(primaryHeading);
this._primaryHeading.setAttribute('aria-label', primaryHeading);
let secondaryHeading = `@${this._comments[0].userName}`;
$(this._secondaryHeading).safeInnerHtml(secondaryHeading);
const actionsContainer = $('.review-actions').appendTo(this._headElement);
this._actionbarWidget = new ActionBar(actionsContainer, {});
this._disposables.push(this._actionbarWidget);
this._actionbarWidget.push(new Action('review.expand', nls.localize('label.expand', "Expand"), 'expand-review-action octicon octicon-chevron-down', true, () => {
this._bodyElement.style.display = 'block';
return null;
}), { label: false, icon: true });
this._actionbarWidget.push(new Action('review.close', nls.localize('label.close', "Close"), 'close-review-action', true, () => {
this.dispose();
return null;
}), { label: false, icon: true });
}
display(comments: modes.Comment[], lineNumber: number) {
this.show({ lineNumber: lineNumber, column: 1 }, 2);
this._bodyElement.style.display = 'none';
for (let i = 0; i < comments.length; i++) {
let singleCommentContainer = document.createElement('div');
singleCommentContainer.className = 'review-comment-contents';
singleCommentContainer.className = 'review-comment';
let avatar = document.createElement('span');
avatar.className = 'float-left';
let img = document.createElement('img');
img.className = 'avatar';
img.src = comments[i].gravatar;
avatar.appendChild(img);
let commentDetailsContainer = document.createElement('div');
commentDetailsContainer.className = 'review-comment-contents';
singleCommentContainer.appendChild(avatar);
singleCommentContainer.appendChild(commentDetailsContainer);
let header = document.createElement('h4');
let author = document.createElement('strong');
author.className = 'author';
......@@ -80,17 +145,17 @@ export class ReviewZoneWidget extends ZoneWidget {
// time.innerText = comments[i].created_at;
header.appendChild(author);
// header.appendChild(time);
singleCommentContainer.appendChild(header);
commentDetailsContainer.appendChild(header);
let body = document.createElement('div');
body.className = 'comment-body';
singleCommentContainer.appendChild(body);
commentDetailsContainer.appendChild(body);
let md = comments[i].body;
body.appendChild(renderMarkdown(md));
this._domNode.appendChild(singleCommentContainer);
this._bodyElement.appendChild(singleCommentContainer);
}
// this._domNode.appendChild(document.createElement('textarea'));
this._resizeObserver = new ResizeObserver(entries => {
if (entries[0].target === this._domNode) {
if (entries[0].target === this._bodyElement) {
const lineHeight = this.editor.getConfiguration().lineHeight;
const arrowHeight = Math.round(lineHeight / 3);
const computedLinesNumber = Math.ceil((entries[0].contentRect.height + arrowHeight + 30) / lineHeight);
......@@ -98,14 +163,25 @@ export class ReviewZoneWidget extends ZoneWidget {
}
});
this._resizeObserver.observe(this._domNode);
this._resizeObserver.observe(this._bodyElement);
}
private _applyTheme(theme: ITheme) {
let borderColor = theme.getColor(peekViewBorder) || Color.transparent;
this.style({
arrowColor: borderColor,
frameColor: borderColor
});
}
dispose() {
super.dispose();
this._resizeObserver.disconnect();
this._resizeObserver = null;
if (this._resizeObserver) {
this._resizeObserver.disconnect();
this._resizeObserver = null;
}
}
}
export class ReviewController implements IEditorContribution {
......@@ -115,12 +191,14 @@ export class ReviewController implements IEditorContribution {
private decorationIDs: string[];
private _domNode: HTMLElement;
private _zoneWidget: ReviewZoneWidget;
private _zoneWidgets: ReviewZoneWidget[];
private _reviewPanelVisible: IContextKey<boolean>;
private _commentThreads: modes.CommentThread[];
constructor(
editor: ICodeEditor,
@IContextKeyService contextKeyService: IContextKeyService
@IContextKeyService contextKeyService: IContextKeyService,
@IThemeService private themeService: IThemeService
) {
this.editor = editor;
this.globalToDispose = [];
......@@ -128,6 +206,7 @@ export class ReviewController implements IEditorContribution {
this.decorationIDs = [];
this.mouseDownInfo = null;
this._commentThreads = [];
this._zoneWidgets = [];
this._reviewPanelVisible = ctxReviewPanelVisible.bindTo(contextKeyService);
this._domNode = document.createElement('div');
......@@ -162,6 +241,11 @@ export class ReviewController implements IEditorContribution {
this._zoneWidget.dispose();
this._zoneWidget = null;
}
this._zoneWidgets.forEach(zone => {
zone.dispose();
});
this._zoneWidgets = [];
this.localToDispose.push(this.editor.onMouseDown(e => this.onEditorMouseDown(e)));
this.localToDispose.push(this.editor.onMouseUp(e => this.onEditorMouseUp(e)));
}
......@@ -180,7 +264,7 @@ export class ReviewController implements IEditorContribution {
let iconClicked = false;
switch (e.target.type) {
case MouseTargetType.GUTTER_LINE_DECORATIONS:
case MouseTargetType.GUTTER_GLYPH_MARGIN:
iconClicked = true;
break;
default:
......@@ -203,7 +287,7 @@ export class ReviewController implements IEditorContribution {
}
if (iconClicked) {
if (e.target.type !== MouseTargetType.GUTTER_LINE_DECORATIONS) {
if (e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN) {
return;
}
}
......@@ -215,7 +299,7 @@ export class ReviewController implements IEditorContribution {
let comments = this.getComments(lineNumber);
if (comments && comments.length) {
this._reviewPanelVisible.set(true);
this._zoneWidget = new ReviewZoneWidget(this.editor);
this._zoneWidget = new ReviewZoneWidget(this.themeService, this.editor, {}, comments);
this._zoneWidget.display(this.getComments(lineNumber), lineNumber);
}
}
......@@ -238,6 +322,17 @@ export class ReviewController implements IEditorContribution {
options: REVIEWL_DECORATION
})));
});
// create viewzones
this._zoneWidgets.forEach(zone => {
zone.dispose();
});
this._commentThreads.forEach(thread => {
let zoneWidget = new ReviewZoneWidget(this.themeService, this.editor, {}, thread.comments);
zoneWidget.display(this.getComments(thread.range.startLineNumber), thread.range.startLineNumber);
});
}
......@@ -286,4 +381,16 @@ function closeReviewPanel(accessor: ServicesAccessor, args: any) {
}
controller.closeWidget();
}
\ No newline at end of file
}
registerThemingParticipant((theme, collector) => {
let editorBackground = theme.getColor(peekViewEditorBackground);
if (editorBackground) {
collector.addRule(
`.monaco-editor .review-widget,` +
`.monaco-editor .review-widget {` +
` background-color: ${editorBackground};` +
`}`);
}
});
......@@ -5006,6 +5006,7 @@ declare namespace monaco.languages {
export interface Comment {
readonly body: IMarkdownString;
readonly userName: string;
readonly gravatar: string;
}
export interface CommentProvider {
......
......@@ -732,6 +732,7 @@ declare module 'vscode' {
interface Comment {
body: MarkdownString;
userName: string;
gravatar: string;
}
/**
......
......@@ -72,6 +72,7 @@ function convertCommentThread(vscodeCommentThread: vscode.CommentThread): modes.
function convertComment(vscodeComment: vscode.Comment): modes.Comment {
return {
body: extHostTypeConverter.MarkdownString.from(vscodeComment.body),
userName: vscodeComment.userName
userName: vscodeComment.userName,
gravatar: vscodeComment.gravatar
};
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册