Merged #253 "Implement configurable tab length support for blob views"
diff --git a/src/main/distrib/data/defaults.properties b/src/main/distrib/data/defaults.properties
index 59fa399..c175068 100644
--- a/src/main/distrib/data/defaults.properties
+++ b/src/main/distrib/data/defaults.properties
@@ -784,6 +784,11 @@
 # BASEFOLDER
 web.projectsFile = ${baseFolder}/projects.conf
 
+# Defines the tab length for all blob views
+#
+# SINCE 1.7.0
+web.tabLength = 4
+
 # Either the full path to a user config file (users.conf)
 # OR a fully qualified class name that implements the IUserService interface.
 #
diff --git a/src/main/java/com/gitblit/service/LuceneService.java b/src/main/java/com/gitblit/service/LuceneService.java
index 3f7635f..798edb0 100644
--- a/src/main/java/com/gitblit/service/LuceneService.java
+++ b/src/main/java/com/gitblit/service/LuceneService.java
@@ -1104,6 +1104,7 @@
 			content = "";

 		}

 

+		int tabLength = storedSettings.getInteger(Keys.web.tabLength, 4);

 		int fragmentLength = SearchObjectType.commit == result.type ? 512 : 150;

 

 		QueryScorer scorer = new QueryScorer(query, "content");

@@ -1126,7 +1127,7 @@
 			if (fragment.length() > fragmentLength) {

 				fragment = fragment.substring(0, fragmentLength) + "...";

 			}

-			return "<pre class=\"text\">" + StringUtils.escapeForHtml(fragment, true) + "</pre>";

+			return "<pre class=\"text\">" + StringUtils.escapeForHtml(fragment, true, tabLength) + "</pre>";

 		}

 

 		// make sure we have unique fragments

diff --git a/src/main/java/com/gitblit/utils/DiffUtils.java b/src/main/java/com/gitblit/utils/DiffUtils.java
index 09b8f80..cdebec1 100644
--- a/src/main/java/com/gitblit/utils/DiffUtils.java
+++ b/src/main/java/com/gitblit/utils/DiffUtils.java
@@ -229,11 +229,12 @@
 	 * @param commit

 	 * @param comparator

 	 * @param outputType

+	 * @param tabLength

 	 * @return the diff

 	 */

 	public static DiffOutput getCommitDiff(Repository repository, RevCommit commit,

-			DiffComparator comparator, DiffOutputType outputType) {

-		return getDiff(repository, null, commit, null, comparator, outputType);

+			DiffComparator comparator, DiffOutputType outputType, int tabLength) {

+		return getDiff(repository, null, commit, null, comparator, outputType, tabLength);

 	}

 

 	/**

@@ -246,11 +247,12 @@
 	 * @param handler

 	 *            to use for rendering binary diffs if {@code outputType} is {@link DiffOutputType#HTML HTML}.

 	 *            May be {@code null}, resulting in the default behavior.

+	 * @param tabLength

 	 * @return the diff

 	 */

 	public static DiffOutput getCommitDiff(Repository repository, RevCommit commit,

-			DiffComparator comparator, DiffOutputType outputType, BinaryDiffHandler handler) {

-		return getDiff(repository, null, commit, null, comparator, outputType, handler);

+			DiffComparator comparator, DiffOutputType outputType, BinaryDiffHandler handler, int tabLength) {

+		return getDiff(repository, null, commit, null, comparator, outputType, handler, tabLength);

 	}

 

 

@@ -263,11 +265,12 @@
 	 * @param path

 	 * @param comparator

 	 * @param outputType

+	 * @param tabLength

 	 * @return the diff

 	 */

 	public static DiffOutput getDiff(Repository repository, RevCommit commit, String path,

-			DiffComparator comparator, DiffOutputType outputType) {

-		return getDiff(repository, null, commit, path, comparator, outputType);

+			DiffComparator comparator, DiffOutputType outputType, int tabLength) {

+		return getDiff(repository, null, commit, path, comparator, outputType, tabLength);

 	}

 

 	/**

@@ -282,11 +285,12 @@
 	 * @param handler

 	 *            to use for rendering binary diffs if {@code outputType} is {@link DiffOutputType#HTML HTML}.

 	 *            May be {@code null}, resulting in the default behavior.

+	 * @param tabLength

 	 * @return the diff

 	 */

 	public static DiffOutput getDiff(Repository repository, RevCommit commit, String path,

-			DiffComparator comparator, DiffOutputType outputType, BinaryDiffHandler handler) {

-		return getDiff(repository, null, commit, path, comparator, outputType, handler);

+			DiffComparator comparator, DiffOutputType outputType, BinaryDiffHandler handler, int tabLength) {

+		return getDiff(repository, null, commit, path, comparator, outputType, handler, tabLength);

 	}

 

 	/**

@@ -297,11 +301,13 @@
 	 * @param commit

 	 * @param comparator

 	 * @param outputType

+	 * @param tabLength

+	 *

 	 * @return the diff

 	 */

 	public static DiffOutput getDiff(Repository repository, RevCommit baseCommit, RevCommit commit,

-			DiffComparator comparator, DiffOutputType outputType) {

-		return getDiff(repository, baseCommit, commit, null, comparator, outputType);

+			DiffComparator comparator, DiffOutputType outputType, int tabLength) {

+		return getDiff(repository, baseCommit, commit, null, comparator, outputType, tabLength);

 	}

 

 	/**

@@ -315,11 +321,12 @@
 	 * @param handler

 	 *            to use for rendering binary diffs if {@code outputType} is {@link DiffOutputType#HTML HTML}.

 	 *            May be {@code null}, resulting in the default behavior.

+	 * @param tabLength

 	 * @return the diff

 	 */

 	public static DiffOutput getDiff(Repository repository, RevCommit baseCommit, RevCommit commit,

-			DiffComparator comparator, DiffOutputType outputType, BinaryDiffHandler handler) {

-		return getDiff(repository, baseCommit, commit, null, comparator, outputType, handler);

+			DiffComparator comparator, DiffOutputType outputType, BinaryDiffHandler handler, int tabLength) {

+		return getDiff(repository, baseCommit, commit, null, comparator, outputType, handler, tabLength);

 	}

 

 	/**

@@ -335,11 +342,12 @@
 	 *            or folder. if unspecified, the diff is for the entire commit.

 	 * @param outputType

 	 * @param diffComparator

+	 * @param tabLength

 	 * @return the diff

 	 */

 	public static DiffOutput getDiff(Repository repository, RevCommit baseCommit, RevCommit commit,

-			String path, DiffComparator diffComparator, DiffOutputType outputType) {

-		return getDiff(repository, baseCommit, commit, path, diffComparator, outputType, null);

+			String path, DiffComparator diffComparator, DiffOutputType outputType, int tabLength) {

+		return getDiff(repository, baseCommit, commit, path, diffComparator, outputType, null, tabLength);

 	}

 

 	/**

@@ -358,10 +366,11 @@
 	 * @param handler

 	 *            to use for rendering binary diffs if {@code outputType} is {@link DiffOutputType#HTML HTML}.

 	 *            May be {@code null}, resulting in the default behavior.

+	 * @param tabLength

 	 * @return the diff

 	 */

 	public static DiffOutput getDiff(Repository repository, RevCommit baseCommit, RevCommit commit, String path,

-			DiffComparator comparator, DiffOutputType outputType, final BinaryDiffHandler handler) {

+			DiffComparator comparator, DiffOutputType outputType, final BinaryDiffHandler handler, int tabLength) {

 		DiffStat stat = null;

 		String diff = null;

 		try {

@@ -370,7 +379,7 @@
 			DiffFormatter df;

 			switch (outputType) {

 			case HTML:

-				df = new GitBlitDiffFormatter(commit.getName(), path, handler);

+				df = new GitBlitDiffFormatter(commit.getName(), path, handler, tabLength);

 				break;

 			case PLAIN:

 			default:

diff --git a/src/main/java/com/gitblit/utils/GitBlitDiffFormatter.java b/src/main/java/com/gitblit/utils/GitBlitDiffFormatter.java
index 35549d4..86b7ca2 100644
--- a/src/main/java/com/gitblit/utils/GitBlitDiffFormatter.java
+++ b/src/main/java/com/gitblit/utils/GitBlitDiffFormatter.java
@@ -127,6 +127,8 @@
 	/** If {@link #truncated}, contains all entries skipped. */
 	private final List<DiffEntry> skipped = new ArrayList<DiffEntry>();
 
+	private int tabLength;
+
 	/**
 	 * A {@link ResettableByteArrayOutputStream} that intercept the "Binary files differ" message produced
 	 * by the super implementation. Unfortunately the super implementation has far too many things private;
@@ -162,11 +164,12 @@
 
 	}
 
-	public GitBlitDiffFormatter(String commitId, String path, BinaryDiffHandler handler) {
+	public GitBlitDiffFormatter(String commitId, String path, BinaryDiffHandler handler, int tabLength) {
 		super(new DiffOutputStream());
 		this.os = (DiffOutputStream) getOutputStream();
 		this.os.setFormatter(this, handler);
 		this.diffStat = new DiffStat(commitId);
+		this.tabLength = tabLength;
 		// If we have a full commitdiff, install maxima to avoid generating a super-long diff listing that
 		// will only tax the browser too much.
 		maxDiffLinesPerFile = path != null ? -1 : getLimit(DIFF_LIMIT_PER_FILE_KEY, 500, DIFF_LIMIT_PER_FILE);
@@ -453,14 +456,14 @@
 			// Highlight trailing whitespace on deleted/added lines.
 			Matcher matcher = trailingWhitespace.matcher(line);
 			if (matcher.find()) {
-				StringBuilder result = new StringBuilder(StringUtils.escapeForHtml(line.substring(0, matcher.start()), CONVERT_TABS));
+				StringBuilder result = new StringBuilder(StringUtils.escapeForHtml(line.substring(0, matcher.start()), CONVERT_TABS, tabLength));
 				result.append("<span class='trailingws-").append(prefix == '+' ? "add" : "sub").append("'>");
 				result.append(StringUtils.escapeForHtml(matcher.group(1), false));
 				result.append("</span>");
 				return result.toString();
 			}
 		}
-		return StringUtils.escapeForHtml(line, CONVERT_TABS);
+		return StringUtils.escapeForHtml(line, CONVERT_TABS, tabLength);
 	}
 
 	/**
@@ -493,7 +496,7 @@
 					} else {
 						sb.append("<th class='diff-state diff-state-sub'></th><td class=\"diff-cell remove2\">");
 					}
-					line = StringUtils.escapeForHtml(line.substring(1), CONVERT_TABS);
+					line = StringUtils.escapeForHtml(line.substring(1), CONVERT_TABS, tabLength);
 				}
 				sb.append(line);
 				if (gitLinkDiff) {
diff --git a/src/main/java/com/gitblit/utils/StringUtils.java b/src/main/java/com/gitblit/utils/StringUtils.java
index 087de54..643c52c 100644
--- a/src/main/java/com/gitblit/utils/StringUtils.java
+++ b/src/main/java/com/gitblit/utils/StringUtils.java
@@ -79,6 +79,19 @@
 	 * @return plain text escaped for html

 	 */

 	public static String escapeForHtml(String inStr, boolean changeSpace) {

+		return escapeForHtml(inStr, changeSpace, 4);

+	}

+

+	/**

+	 * Prepare text for html presentation. Replace sensitive characters with

+	 * html entities.

+	 *

+	 * @param inStr

+	 * @param changeSpace

+	 * @param tabLength

+	 * @return plain text escaped for html

+	 */

+	public static String escapeForHtml(String inStr, boolean changeSpace, int tabLength) {

 		StringBuilder retStr = new StringBuilder();

 		int i = 0;

 		while (i < inStr.length()) {

@@ -93,7 +106,9 @@
 			} else if (changeSpace && inStr.charAt(i) == ' ') {

 				retStr.append("&nbsp;");

 			} else if (changeSpace && inStr.charAt(i) == '\t') {

-				retStr.append(" &nbsp; &nbsp;");

+				for (int j = 0; j < tabLength; j++) {

+					retStr.append("&nbsp;");

+				}

 			} else {

 				retStr.append(inStr.charAt(i));

 			}

diff --git a/src/main/java/com/gitblit/wicket/pages/BlamePage.java b/src/main/java/com/gitblit/wicket/pages/BlamePage.java
index 3c850f2..e45bbbc 100644
--- a/src/main/java/com/gitblit/wicket/pages/BlamePage.java
+++ b/src/main/java/com/gitblit/wicket/pages/BlamePage.java
@@ -154,6 +154,7 @@
 

 		add(new Label("missingBlob").setVisible(false));

 

+		final int tabLength = app().settings().getInteger(Keys.web.tabLength, 4);

 		List<AnnotatedLine> lines = DiffUtils.blame(getRepository(), blobPath, objectId);

 		final Map<?, String> colorMap = initializeColors(activeBlameType, lines);

 		ListDataProvider<AnnotatedLine> blameDp = new ListDataProvider<AnnotatedLine>(lines);

@@ -212,7 +213,7 @@
 					color = colorMap.get(entry.commitId);

 					break;

 				}

-				Component data = new Label("data", StringUtils.escapeForHtml(entry.data, true)).setEscapeModelStrings(false);

+				Component data = new Label("data", StringUtils.escapeForHtml(entry.data, true, tabLength)).setEscapeModelStrings(false);

 				data.add(new SimpleAttributeModifier("style", "background-color: " + color + ";"));

 				item.add(data);

 			}

diff --git a/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java b/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java
index 187e460..adf815e 100644
--- a/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java
@@ -57,7 +57,7 @@
 			RevCommit parent = commit.getParentCount() == 0 ? null : commit.getParent(0);

 			ImageDiffHandler handler = new ImageDiffHandler(this, repositoryName,

 					parent.getName(), commit.getName(), imageExtensions);

-			diff = DiffUtils.getDiff(r, commit, blobPath, diffComparator, DiffOutputType.HTML, handler).content;

+			diff = DiffUtils.getDiff(r, commit, blobPath, diffComparator, DiffOutputType.HTML, handler, 3).content;

 			if (handler.getImgDiffCount() > 0) {

 				addBottomScript("scripts/imgdiff.js"); // Tiny support script for image diffs

 			}

@@ -68,7 +68,7 @@
 			RevCommit baseCommit = JGitUtils.getCommit(r, baseObjectId);

 			ImageDiffHandler handler = new ImageDiffHandler(this, repositoryName,

 					baseCommit.getName(), commit.getName(), imageExtensions);

-			diff = DiffUtils.getDiff(r, baseCommit, commit, blobPath, diffComparator, DiffOutputType.HTML, handler).content;

+			diff = DiffUtils.getDiff(r, baseCommit, commit, blobPath, diffComparator, DiffOutputType.HTML, handler, 3).content;

 			if (handler.getImgDiffCount() > 0) {

 				addBottomScript("scripts/imgdiff.js"); // Tiny support script for image diffs

 			}

diff --git a/src/main/java/com/gitblit/wicket/pages/BlobPage.java b/src/main/java/com/gitblit/wicket/pages/BlobPage.java
index df30e4c..1ef8f22 100644
--- a/src/main/java/com/gitblit/wicket/pages/BlobPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/BlobPage.java
@@ -195,7 +195,8 @@
 		} else {

 			sb.append("<pre class=\"plainprint\">");

 		}

-		lines = StringUtils.escapeForHtml(source, true).split("\n");

+		final int tabLength = app().settings().getInteger(Keys.web.tabLength, 4);

+		lines = StringUtils.escapeForHtml(source, true, tabLength).split("\n");

 

 		sb.append("<table width=\"100%\"><tbody>");

 

diff --git a/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java
index 95580ed..3754f3e 100644
--- a/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java
@@ -87,8 +87,8 @@
 		final List<String> imageExtensions = app().settings().getStrings(Keys.web.imageExtensions);
 		final ImageDiffHandler handler = new ImageDiffHandler(this, repositoryName,
 				parents.isEmpty() ? null : parents.get(0), commit.getName(), imageExtensions);
-
-		final DiffOutput diff = DiffUtils.getCommitDiff(r, commit, diffComparator, DiffOutputType.HTML, handler);
+		final int tabLength = app().settings().getInteger(Keys.web.tabLength, 4);
+		final DiffOutput diff = DiffUtils.getCommitDiff(r, commit, diffComparator, DiffOutputType.HTML, handler, tabLength);
 		if (handler.getImgDiffCount() > 0) {
 			addBottomScript("scripts/imgdiff.js"); // Tiny support script for image diffs
 		}
diff --git a/src/main/java/com/gitblit/wicket/pages/ComparePage.java b/src/main/java/com/gitblit/wicket/pages/ComparePage.java
index d171044..7e7ac2f 100644
--- a/src/main/java/com/gitblit/wicket/pages/ComparePage.java
+++ b/src/main/java/com/gitblit/wicket/pages/ComparePage.java
@@ -119,9 +119,9 @@
 			final List<String> imageExtensions = app().settings().getStrings(Keys.web.imageExtensions);
 			final ImageDiffHandler handler = new ImageDiffHandler(this, repositoryName,
 					fromCommit.getName(), toCommit.getName(), imageExtensions);
-
 			final DiffComparator diffComparator = WicketUtils.getDiffComparator(params);
-			final DiffOutput diff = DiffUtils.getDiff(r, fromCommit, toCommit, diffComparator, DiffOutputType.HTML, handler);
+			final int tabLength = app().settings().getInteger(Keys.web.tabLength, 4);
+			final DiffOutput diff = DiffUtils.getDiff(r, fromCommit, toCommit, diffComparator, DiffOutputType.HTML, handler, tabLength);
 			if (handler.getImgDiffCount() > 0) {
 				addBottomScript("scripts/imgdiff.js"); // Tiny support script for image diffs
 			}
diff --git a/src/main/java/com/gitblit/wicket/pages/ReviewProposalPage.java b/src/main/java/com/gitblit/wicket/pages/ReviewProposalPage.java
index 8aec9e6..ceca1ec 100644
--- a/src/main/java/com/gitblit/wicket/pages/ReviewProposalPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/ReviewProposalPage.java
@@ -76,8 +76,9 @@
 		sb.append(asParam(p, proposal.name, "exclude", ""));

 		sb.append(asParam(p, proposal.name, "include", ""));

 

+		final int tabLength = app().settings().getInteger(Keys.web.tabLength, 4);

 		add(new Label("definition", StringUtils.breakLinesForHtml(StringUtils.escapeForHtml(sb

-				.toString().trim(), true))).setEscapeModelStrings(false));

+				.toString().trim(), true, tabLength))).setEscapeModelStrings(false));

 

 		List<RepositoryModel> repositories = new ArrayList<RepositoryModel>(

 				proposal.repositories.values());

diff --git a/src/test/java/com/gitblit/tests/DiffUtilsTest.java b/src/test/java/com/gitblit/tests/DiffUtilsTest.java
index c73e478..e8e839a 100644
--- a/src/test/java/com/gitblit/tests/DiffUtilsTest.java
+++ b/src/test/java/com/gitblit/tests/DiffUtilsTest.java
@@ -41,7 +41,7 @@
 		Repository repository = GitBlitSuite.getHelloworldRepository();

 		RevCommit commit = JGitUtils.getCommit(repository,

 				"1d0c2933a4ae69c362f76797d42d6bd182d05176");

-		String diff = DiffUtils.getCommitDiff(repository, commit, DiffComparator.SHOW_WHITESPACE, DiffOutputType.PLAIN).content;

+		String diff = DiffUtils.getCommitDiff(repository, commit, DiffComparator.SHOW_WHITESPACE, DiffOutputType.PLAIN, 3).content;

 		repository.close();

 		assertTrue(diff != null && diff.length() > 0);

 		String expected = "-		system.out.println(\"Hello World\");\n+		System.out.println(\"Hello World\"";

@@ -55,7 +55,7 @@
 				"8baf6a833b5579384d9b9ceb8a16b5d0ea2ec4ca");

 		RevCommit commit = JGitUtils.getCommit(repository,

 				"1d0c2933a4ae69c362f76797d42d6bd182d05176");

-		String diff = DiffUtils.getDiff(repository, baseCommit, commit, DiffComparator.SHOW_WHITESPACE, DiffOutputType.PLAIN).content;

+		String diff = DiffUtils.getDiff(repository, baseCommit, commit, DiffComparator.SHOW_WHITESPACE, DiffOutputType.PLAIN, 3).content;

 		repository.close();

 		assertTrue(diff != null && diff.length() > 0);

 		String expected = "-		system.out.println(\"Hello World\");\n+		System.out.println(\"Hello World\"";

@@ -67,7 +67,7 @@
 		Repository repository = GitBlitSuite.getHelloworldRepository();

 		RevCommit commit = JGitUtils.getCommit(repository,

 				"1d0c2933a4ae69c362f76797d42d6bd182d05176");

-		String diff = DiffUtils.getDiff(repository, commit, "java.java", DiffComparator.SHOW_WHITESPACE, DiffOutputType.PLAIN).content;

+		String diff = DiffUtils.getDiff(repository, commit, "java.java", DiffComparator.SHOW_WHITESPACE, DiffOutputType.PLAIN, 3).content;

 		repository.close();

 		assertTrue(diff != null && diff.length() > 0);

 		String expected = "-		system.out.println(\"Hello World\");\n+		System.out.println(\"Hello World\"";

diff --git a/src/test/java/com/gitblit/tests/StringUtilsTest.java b/src/test/java/com/gitblit/tests/StringUtilsTest.java
index 0fd42aa..7176b88 100644
--- a/src/test/java/com/gitblit/tests/StringUtilsTest.java
+++ b/src/test/java/com/gitblit/tests/StringUtilsTest.java
@@ -50,7 +50,7 @@
 	public void testEscapeForHtml() throws Exception {

 		String input = "& < > \" \t";

 		String outputNoChange = "&amp; &lt; &gt; &quot; \t";

-		String outputChange = "&amp;&nbsp;&lt;&nbsp;&gt;&nbsp;&quot;&nbsp; &nbsp; &nbsp;";

+		String outputChange = "&amp;&nbsp;&lt;&nbsp;&gt;&nbsp;&quot;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";

 		assertEquals(outputNoChange, StringUtils.escapeForHtml(input, false));

 		assertEquals(outputChange, StringUtils.escapeForHtml(input, true));

 	}