Beispiel #1
0
		/// <summary>
		/// Generates the diff view for two file revisions.
		/// </summary>
		/// <param name="baseFile"> The base file. </param>
		/// <param name="baseId"> The database id of the file. </param>
		/// <param name="baseComments"> The set of comments associated with the base file. </param>
		/// <param name="baseHeader"> The caption for the base file column. </param>
		/// <param name="diffFile"> The diff file. </param>
		/// <param name="diffId"> The database id of the diff. </param>
		/// <param name="diffComments"> The set of comments associated with the diff file. </param>
		/// <param name="diffHeader"> The caption for the changed file column. </param>
		/// <param name="baseIsLeft"> True if the base file is left column. </param>
		/// <param name="rawDiff"> Stream containing raw diff.exe output. </param>
		/// <param name="fileName"> The name of the file being compared. </param>
		private Control GenerateFileDiffView(
			StreamCombiner baseFile, int baseId, AbstractedComment[] baseComments, string baseHeader,
			StreamCombiner diffFile, int diffId, AbstractedComment[] diffComments, string diffHeader,
			bool baseIsLeft, StreamCombiner rawDiff, string fileName)
		{
			bool isSingleFileView = baseId == diffId;

			{   // Get user-configurable settings
				UserContext uc = CurrentUserContext;

				DiffViewOptions = new FileDiffViewOptions()
				{
					IsBaseLeft = baseIsLeft,
					IsUnified = isSingleFileView ? false : uc.UnifiedDiffView ?? false,
					OmitUnchangedLines = (Request.QueryString["showAllLines"] ?? "false") != "true",
					CommentClickMode = uc.CommentClickMode,
					AutoCollapseComments = uc.AutoCollapseComments ?? true, // default to auto collapse
				};
			}

			ILineEncoder baseEncoder = GetEncoderForFile(fileName);
			ILineEncoder diffEncoder = GetEncoderForFile(fileName);

			Master.FindControl<Panel>("RootDivElement").Style[HtmlTextWriterStyle.Width] = "95%";

			#region View table initialization
			TableGen.Table fileView;
			if (isSingleFileView)
			{   // Single file view
				fileView = new TableGen.Table(new string[2] { "Num Base", "Txt Base" })
				{
					ID = "fileview",
					EnableViewState = false,
					CssClass = "CssFileView CssFileViewSingle"
				};
				fileView.ColumnGroup.ColumnNameIndexMap = new KeyValuePair<string, int>[4]
				{
					new KeyValuePair<string, int>("Num Base", 0),
					new KeyValuePair<string, int>("Txt Base", 1),
					new KeyValuePair<string, int>("Num Diff", 0),
					new KeyValuePair<string, int>("Txt Diff", 1),
				};
			}
			else if (DiffViewOptions.IsSplit)
			{   // Split file diff
				fileView = new TableGen.Table(new string[4]
				{
					"Num " + (DiffViewOptions.IsBaseLeft ? "Base" : "Diff"),
					"Txt " + (DiffViewOptions.IsBaseLeft ? "Base" : "Diff"),
					"Num " + (DiffViewOptions.IsBaseLeft ? "Diff" : "Base"),
					"Txt " + (DiffViewOptions.IsBaseLeft ? "Diff" : "Base"),
				});
			}
			else
			{   // Inline file diff
				fileView = new TableGen.Table(new string[3]
				{
					"Num " + (DiffViewOptions.IsBaseLeft ? "Base" : "Diff"),
					"Num " + (DiffViewOptions.IsBaseLeft ? "Diff" : "Base"),
					"Txt",
				});
				fileView.ColumnGroup.ColumnNameIndexMap = new KeyValuePair<string, int>[4]
				{
					new KeyValuePair<string, int>("Num " + (DiffViewOptions.IsBaseLeft ? "Base" : "Diff"), 0),
					new KeyValuePair<string, int>("Num " + (DiffViewOptions.IsBaseLeft ? "Diff" : "Base"), 1),
					new KeyValuePair<string, int>("Txt Base", 2),
					new KeyValuePair<string, int>("Txt Diff", 2),
				};
			}

			fileView.AppendCSSClass("CssFileView");
			if (!isSingleFileView)
				fileView.AppendCSSClass(DiffViewOptions.IsSplit ? "CssFileViewSplit" : "CssFileViewUnified");
			fileView.EnableViewState = false;
			fileView.ID = "fileview";
			fileView.Attributes["maxLineLen"] = MaxLineLength.ToString();

			AddJScriptCreateCommentOnClickAttribute(fileView);

			// Add the table header
			var fileViewHeaderGroup = fileView.CreateRowGroup();
			fileViewHeaderGroup.IsHeader = true;
			fileView.Add(fileViewHeaderGroup);

			var fileViewHeader = new TableGen.Row(isSingleFileView ? 1 : 2);
			fileViewHeader[0].ColumnSpan = 2;

			fileViewHeader[DiffViewOptions.IsSplit ? 0 : 1].Add(new HyperLink()
			{
				Text = baseHeader,
				NavigateUrl = Request.FilePath + "?vid=" + baseId,
			});

			if (!isSingleFileView)
			{
				fileViewHeader[1].ColumnSpan = 2;
				fileViewHeader[1].Add(new HyperLink()
				{
					Text = diffHeader,
					NavigateUrl = Request.FilePath + "?vid=" + diffId,
				});
			}

			fileViewHeader.AppendCSSClass("Header");
			fileViewHeaderGroup.AddRow(fileViewHeader);

			#endregion

			var baseFileInfo = new DiffFileInfo(baseFile, baseEncoder, baseId, baseComments, BaseOrDiff.Base);
			var diffFileInfo = new DiffFileInfo(diffFile, diffEncoder, diffId, diffComments, BaseOrDiff.Diff);

			int curRowNum = 1;
			int curRowGroupNum = 1;

			string baseScriptIdPrefix = baseFileInfo.ScriptId;
			string diffScriptIdPrefix = diffFileInfo.ScriptId;

			foreach (var diffItem in DiffItem.EnumerateDifferences(rawDiff))
			{
				bool atStart = diffItem.BaseStartLineNumber == 1;
				bool atEnd = diffItem.BaseLineCount == int.MaxValue;

				var baseLines = new List<LineAndComments>();
				for (int i = 0; i < diffItem.BaseLineCount && baseFileInfo.MoveNextLine(); ++i)
					baseLines.Add(GetLineAndComments(baseFileInfo));

				var diffLines = new List<LineAndComments>();
				if (isSingleFileView)
				{
					diffLines = baseLines;
				}
				else
				{
					for (int i = 0; i < diffItem.DiffLineCount && diffFileInfo.MoveNextLine(); ++i)
						diffLines.Add(GetLineAndComments(diffFileInfo));
				}

				var baseLinesLength = baseLines.Count();
				var diffLinesLength = diffLines.Count();

				// The end is the only case where the DiffInfo line counts may be incorrect. If there are in fact
				// zero lines then just continue, which should cause the foreach block to end and we'll continue
				// like the DiffItem never existed.
				if (atEnd && diffItem.DiffType == DiffType.Unchanged && baseLinesLength == 0 && diffLinesLength == 0)
					continue;

				var curGroup = fileView.CreateRowGroup();
				curGroup.AppendCSSClass(diffItem.DiffType.ToString());
				curGroup.ID = "rowgroup" + (curRowGroupNum++).ToString();
				fileView.AddItem(curGroup);

				var numPasses = 1;
				if (DiffViewOptions.IsUnified && diffItem.DiffType != DiffType.Unchanged)
					numPasses = 2;

				for (int pass = 1; pass <= numPasses; ++pass)
				{
					int lastLineWithComment = 0;
					int nextLineWithComment = 0;
					for (int i = 0; i < Math.Max(baseLinesLength, diffLinesLength); ++i)
					{
						var row = curGroup.CreateRow();
						if (pass == 1)
						{
							if (DiffViewOptions.IsUnified && diffItem.DiffType != DiffType.Unchanged)
								row.AppendCSSClass("Base");
						}
						else if (pass == 2)
						{
							Debug.Assert(DiffViewOptions.IsUnified);
							if (DiffViewOptions.IsUnified && diffItem.DiffType != DiffType.Unchanged)
								row.AppendCSSClass("Diff");
						}

						if (pass == 1)
						{
							// Check if we should omit any lines.
							if (diffItem.DiffType == DiffType.Unchanged &&
								DiffViewOptions.OmitUnchangedLines)
							{
								int contextLineCount = 50;
								if (baseLinesLength >= ((atStart || atEnd) ? contextLineCount : contextLineCount*2))
								{
									if (i >= nextLineWithComment)
									{
										lastLineWithComment = nextLineWithComment;
										if (isSingleFileView)
										{
											nextLineWithComment = baseLines.IndexOfFirst(x => !x.Comments.IsNullOrEmpty(), i);
										}
										else
										{
											nextLineWithComment = Math.Min(
												baseLines.IndexOfFirst(x => !x.Comments.IsNullOrEmpty(), i),
												diffLines.IndexOfFirst(x => !x.Comments.IsNullOrEmpty(), i));
										}
									}
									if (((atStart && i == 0) || (i - lastLineWithComment == contextLineCount)) &&
										((atEnd && nextLineWithComment == baseLinesLength) || (nextLineWithComment - i > contextLineCount)))
									{
										// Skip a bunch of lines!
										row = GetUnchangedLinesBreak(fileView.ColumnCount);
										row.ID = "row" + (curRowNum++).ToString();
										curGroup.AddItem(row);
										i = nextLineWithComment - ((atEnd && nextLineWithComment == baseLinesLength) ? 0 : 50);
										continue;
									}
								}
							}
						}

						if (i < baseLinesLength && pass == 1)
						{
							string scriptId = baseScriptIdPrefix + baseLines[i].LineNum.ToString();
							row["Num Base"].ID = scriptId + "_linenumber";
							row["Num Base"].Text = baseLines[i].LineNum.ToString();
							row["Txt Base"].ID = scriptId;
							row["Txt Base"].Add(EncodeLineTextAndComments(baseFileInfo.Encoder, "base", baseLines[i]));
						}
						if (i < diffLinesLength && !isSingleFileView)
						{
							string scriptId = diffScriptIdPrefix + diffLines[i].LineNum.ToString();
							if (DiffViewOptions.IsSplit || pass == 2 || (pass == 1 && diffItem.DiffType == DiffType.Unchanged))
							{
								row["Num Diff"].ID = scriptId + "_linenumber";
								row["Num Diff"].Text = diffLines[i].LineNum.ToString();
							}
							if (DiffViewOptions.IsSplit || pass == 2)
							{
								row["Txt Diff"].ID = scriptId;
								row["Txt Diff"].Add(EncodeLineTextAndComments(diffFileInfo.Encoder, "diff", diffLines[i]));
							}
						}
						row.ID = "row" + (curRowNum++).ToString();
						curGroup.AddItem(row);
					}
				}
			}

			encoderStyles = baseEncoder.GetEncoderCssStream();

			baseEncoder.Dispose();
			diffEncoder.Dispose();

			return fileView;
		}
Beispiel #2
0
		/// <summary>
		/// Writes out one line and comments, if any.
		/// </summary>
		/// <param name="cell"> The table cell to use. </param>
		/// <param name="line"> Current line. </param>
		/// <param name="comments"> Comments. </param>
		/// <param name="commentIndex"> [In/Out] Current index into comments array. </param>
		private IEnumerable<Control> EncodeLineTextAndComments(
			ILineEncoder encoder,
			string prefix,
			int lineNumber,
			string lineText,
			AbstractedComment[] comments,
			ref int commentIndex)
		{
			StringBuilder lineHtml = new StringBuilder(encoder.EncodeLine(lineText, int.MaxValue, TabValue));

			StringBuilder commentsHtml = new StringBuilder();
			while (commentIndex < comments.Length && comments[commentIndex].Line == lineNumber)
			{
				AddCommentToCell(commentsHtml, comments[commentIndex], prefix);
				++commentIndex;
			}

			return new Control[2]
			{
				new Literal() { Text = "<pre class=\"Code\">" + lineHtml.ToString() + "</pre>" },
				new Literal() { Text = commentsHtml.ToString() }
			};
		}
Beispiel #3
0
			/// <summary>
			/// Creates a DiffFileInfo for a file being compared.
			/// </summary>
			/// <param name="file">The file stream.</param>
			/// <param name="encoder">The line encoder.</param>
			/// <param name="id">The file ID within the database.</param>
			/// <param name="comments">The array of comments for the file.</param>
			/// <param name="baseOrDiff">What role the file plays within the comparison.</param>
			public DiffFileInfo(
				StreamCombiner file,
				ILineEncoder encoder,
				int id,
				AbstractedComment[] comments,
				BaseOrDiff baseOrDiff)
			{
				File = file;
				Encoder = encoder;
				Id = id;
				ScriptId = baseOrDiff.ToString().ToLowerCultureInvariant() + "_" + Id.ToString() + "_";
				Comments = comments;
				//CurLineNum = 0;
				//NextCommentIndex = 0;
				BaseOrDiff = baseOrDiff;
			}
Beispiel #4
0
		/// <summary>
		/// Appends a comment. The HTML structure produced by this function MUST be kept in sync with the one
		/// produced by createNewComment in comments.js.
		/// </summary>
		/// <param name="sb"> The StringBuilder where the comment should be appended. </param>
		/// <param name="comment"> The comment which should be appended. </param>
		private void AddCommentToCell(StringBuilder sbOut, AbstractedComment comment, string prefix)
		{
			if (comment.UserName.EqualsIgnoreCase(AuthenticatedUserAlias))
				HintsData.HaveCommentsByOthers = true;
			else
				HintsData.HaveCommentsByUser = true;

			StringBuilder sb = new StringBuilder();
			string commentId = comment.JavaScriptId(prefix);
			string commentText = Server.HtmlEncode(comment.CommentText);

			// This is mirrored in comments.js to maintain consistent spacing.
			// IE does not properly obey margin top and bottom values in CSS,
			// so use conditional IE comments to insert directly.
			sb.Append(
	"<div id=\"{commentId}\" class=\"Comment\">");

			if (!comment.IsReadOnly)
				sb.Append(
	  "<div class=\"Edit\" style=\"display:none;\">" +
		"<textarea rows=\"6\" cols=\"0\" " +
				  "onkeyup=\"textAreaAutoSize(\'{commentId}\');\">{commentText}</textarea>" +
		"<div class=\"Buttons\">" +
		  "<input class=\"button\" type=\"button\" value=\"submit\" " +
				  "onclick=\"onSubmitComment(event);\"/>" +
		  "<input class=\"button\" type=\"button\" value=\"cancel\" " +
				  "onclick=\"onCancelComment(event);\"/>" +
		  "<input class=\"button\" type=\"button\" value=\"remove\" " +
				  "onclick=\"onRemoveComment(event);\"/>" +
		"</div>" +
	  "</div>");

			sb.Append(
	  "<div class=\"Display\">" +
		"<div class=\"Header\">" +
		  "{userName} on {timeStamp}:" +
		"</div>" +
		"<div class=\"Body\">" +
		  "{commentText}" +
		"</div>" +
	  "</div>" +
	"</div>");

			sb.Replace("{commentId}", commentId + "_comment");
			sb.Replace("{userName}", comment.UserName);
			sb.Replace("{commentText}", commentText);
			sb.Replace("{timeStamp}", WrapTimeStamp(comment.TimeStamp));

			sbOut.Append(sb);
		}