diff --git a/build.sbt b/build.sbt index 10723d9..26b4414 100644 --- a/build.sbt +++ b/build.sbt @@ -68,7 +68,8 @@ "com.wix" % "wix-embedded-mysql" % "3.0.0" % "test", "ru.yandex.qatools.embed" % "postgresql-embedded" % "2.6" % "test", "net.i2p.crypto" % "eddsa" % "0.2.0", - "is.tagomor.woothee" % "woothee-java" % "1.7.0" + "is.tagomor.woothee" % "woothee-java" % "1.7.0", + "org.ec4j.core" % "ec4j-core" % "0.0.1" ) // Compiler settings diff --git a/src/main/java/editorconfig/JGitResource.java b/src/main/java/editorconfig/JGitResource.java new file mode 100644 index 0000000..5590d9f --- /dev/null +++ b/src/main/java/editorconfig/JGitResource.java @@ -0,0 +1,134 @@ +package editorconfig; + +import org.ec4j.core.Resource; +import org.ec4j.core.ResourcePath; +import org.ec4j.core.model.Ec4jPath; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevTree; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.treewalk.TreeWalk; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; + +public class JGitResource implements Resource { + private final Repository repo; + private final String revStr; + + Ec4jPath path; + + private static String removeInitialSlash(Ec4jPath path) { + return Ec4jPath.Ec4jPaths.root().relativize(path).toString(); + } + + public JGitResource(Git git, String revStr, String path){ + if (!path.startsWith("/")){ + path = "/" + path; + } + this.repo= git.getRepository(); + this.path = Ec4jPath.Ec4jPaths.of(path); + this.revStr = revStr; + } + + public JGitResource(Repository repo, String revStr, String path){ + if (!path.startsWith("/")){ + path = "/" + path; + } + this.repo = repo; + this.path = Ec4jPath.Ec4jPaths.of(path); + this.revStr = revStr; + } + + + public JGitResource(Repository repo, String revStr, Ec4jPath path){ + this.repo = repo; + this.path = path; + this.revStr = revStr; + } + + private RevTree getRevTree() throws IOException { + ObjectReader reader = repo.newObjectReader(); + try { + RevWalk revWalk = new RevWalk(reader); + ObjectId id = repo.resolve(revStr); + RevCommit commit = revWalk.parseCommit(id); + return commit.getTree(); + } finally { + reader.close(); + } + } + + @Override + public boolean exists() { + ObjectReader reader = repo.newObjectReader(); + try { + TreeWalk treeWalk = TreeWalk.forPath(reader, removeInitialSlash(path), getRevTree()); + if (treeWalk != null){ + return true; + } + else { + return false; + } + } catch (IOException e) { + return false; + } finally { + reader.close(); + } + } + + @Override + public ResourcePath getParent() { + Ec4jPath parent = path.getParentPath(); + return parent == null ? null : new JGitResourcePath(repo, revStr, path.getParentPath()); + } + + @Override + public Ec4jPath getPath() { + return path; + } + + @Override + public RandomReader openRandomReader() throws IOException { + return Resources.StringRandomReader.ofReader(openReader()); + } + + @Override + public Reader openReader() throws IOException { + ObjectReader reader = repo.newObjectReader(); + try { + TreeWalk treeWalk = TreeWalk.forPath(reader, removeInitialSlash(path), getRevTree()); + return new InputStreamReader(reader.open(treeWalk.getObjectId(0)).openStream(), StandardCharsets.UTF_8); + } finally { + reader.close(); + } + } + + @Override + public boolean equals(Object obj){ + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + JGitResource other = (JGitResource) obj; + if (!repo.equals(other.repo) || !revStr.equals(other.revStr) || !path.equals(other.path)){ + return false; + } + return true; + } + + @Override + public String toString(){ + return "JGitResouce(Repo:" + repo.getDirectory() + ", revStr:" + revStr + ", path:" + path.toString() + ")"; + } +} diff --git a/src/main/java/editorconfig/JGitResourcePath.java b/src/main/java/editorconfig/JGitResourcePath.java new file mode 100644 index 0000000..8d99d1e --- /dev/null +++ b/src/main/java/editorconfig/JGitResourcePath.java @@ -0,0 +1,84 @@ +package editorconfig; + +import org.ec4j.core.Resource; +import org.ec4j.core.ResourcePath; +import org.ec4j.core.model.Ec4jPath; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.lib.Repository; + +public class JGitResourcePath implements ResourcePath { + private final Repository repo; + private final String revStr; + private final Ec4jPath path; + + public JGitResourcePath(Repository repo, String revStr, Ec4jPath path){ + this.repo= repo; + this.revStr = revStr; + this.path = path; + } + + public static JGitResourcePath RootDirectory(Git git, String revStr){ + return new JGitResourcePath(git.getRepository(), revStr, Ec4jPath.Ec4jPaths.of("/")); + } + + @Override + public ResourcePath getParent() { + Ec4jPath parent = path.getParentPath(); + return parent == null ? null : new JGitResourcePath(repo, revStr, parent); + } + + @Override + public Ec4jPath getPath() { + return path; + } + + @Override + public boolean hasParent() { + return path.getParentPath() != null; + } + + @Override + public Resource relativize(Resource resource) { + if (resource instanceof JGitResource) { + JGitResource jgitResource = (JGitResource) resource; + return new JGitResource(repo, revStr, path.relativize(jgitResource.path).toString()); + } else { + throw new IllegalArgumentException( + this.getClass().getName() + ".relativize(Resource resource) can handle only instances of " + + JGitResource.class.getName()); + } + } + + @Override + public Resource resolve(String name) { + if(path == null){ + return new JGitResource(repo, revStr, name); + } + else { + return new JGitResource(repo, revStr, path.resolve(name)); + } + } + + @Override + public boolean equals(Object obj){ + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + JGitResourcePath other = (JGitResourcePath) obj; + if (!repo.equals(other.repo) || !revStr.equals(other.revStr) || !path.equals(other.path)){ + return false; + } + return true; + } + + @Override + public String toString(){ + return "JGitResoucePath(Repo:" + repo.getDirectory() + ", revStr:" + revStr + ", path:" + path.toString() + ")"; + } +} diff --git a/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala b/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala index 3da3594..81d4e86 100644 --- a/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala +++ b/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala @@ -1,8 +1,8 @@ package gitbucket.core.controller import java.io.File -import javax.servlet.http.{HttpServletRequest, HttpServletResponse} +import javax.servlet.http.{HttpServletRequest, HttpServletResponse} import gitbucket.core.plugin.PluginRegistry import gitbucket.core.repo.html import gitbucket.core.helper @@ -19,6 +19,7 @@ import gitbucket.core.view.helpers import org.scalatra.forms._ import org.apache.commons.io.FileUtils +import org.ec4j.core.model.PropertyType import org.eclipse.jgit.api.{ArchiveCommand, Git} import org.eclipse.jgit.archive.{TgzFormat, ZipFormat} import org.eclipse.jgit.dircache.{DirCache, DirCacheBuilder} @@ -314,17 +315,23 @@ git => val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch)) - getPathObjectId(git, path, revCommit).map { objectId => - val paths = path.split("/") - html.editor( - branch = branch, - repository = repository, - pathList = paths.take(paths.size - 1).toList, - fileName = Some(paths.last), - content = JGitUtil.getContentInfo(git, path, objectId), - protectedBranch = protectedBranch, - commit = revCommit.getName - ) + getPathObjectId(git, path, revCommit).map { + objectId => + val paths = path.split("/") + val props = EditorConfigUtil.readProperties(git, branch, path) + + html.editor( + branch = branch, + repository = repository, + pathList = paths.take(paths.size - 1).toList, + fileName = Some(paths.last), + content = JGitUtil.getContentInfo(git, path, objectId), + protectedBranch = protectedBranch, + commit = revCommit.getName, + newLineMode = EditorConfigUtil.getNewLineMode(props), + useSoftTabs = EditorConfigUtil.getUseSoftTabs(props), + tabSize = EditorConfigUtil.getTabWidth(props) + ) } getOrElse NotFound() } }) @@ -435,6 +442,8 @@ // Download (This route is left for backword compatibility) responseRawFile(git, objectId, path, repository) } else { + val props = EditorConfigUtil.readProperties(git, id, path) + val tabSize = EditorConfigUtil.getTabWidth(props) html.blob( branch = id, repository = repository, @@ -443,7 +452,8 @@ latestCommit = new JGitUtil.CommitInfo(JGitUtil.getLastModifiedCommit(git, revCommit, path)), hasWritePermission = hasDeveloperRole(repository.owner, repository.name, context.loginAccount), isBlame = request.paths(2) == "blame", - isLfsFile = isLfsFile(git, objectId) + isLfsFile = isLfsFile(git, objectId), + tabSize = tabSize ) } } getOrElse NotFound() diff --git a/src/main/scala/gitbucket/core/util/EditorConfigUtil.scala b/src/main/scala/gitbucket/core/util/EditorConfigUtil.scala new file mode 100644 index 0000000..edc13d8 --- /dev/null +++ b/src/main/scala/gitbucket/core/util/EditorConfigUtil.scala @@ -0,0 +1,46 @@ +package gitbucket.core.util + +import java.nio.charset.StandardCharsets + +import editorconfig.{JGitResource, JGitResourcePath} +import org.ec4j.core.model.PropertyType.{EndOfLineValue, IndentStyleValue} +import org.ec4j.core.model.{PropertyType, Version} +import org.ec4j.core.{EditorConfigConstants, EditorConfigLoader, ResourceProperties, ResourcePropertiesService} +import org.eclipse.jgit.api.Git + +import collection.JavaConverters._ + +object EditorConfigUtil { + def readProperties(git: Git, rev: String, path: String): ResourceProperties = { + val resourcePropertiesService = ResourcePropertiesService + .builder() + .configFileName(EditorConfigConstants.EDITORCONFIG) + .rootDirectory(JGitResourcePath.RootDirectory(git, rev)) + .loader(EditorConfigLoader.of(Version.CURRENT)) + .keepUnset(true) + .build() + + resourcePropertiesService.queryProperties(new JGitResource(git, rev, path)) + } + + def getTabWidth(props: ResourceProperties): Int = { + props.getValue[Integer](PropertyType.tab_width, 8, false) + } + + def getNewLineMode(props: ResourceProperties): String = { + props.getValue[EndOfLineValue](PropertyType.end_of_line, null, false) match { + case EndOfLineValue.cr => "cr" + case EndOfLineValue.lf => "lf" + case EndOfLineValue.crlf => "crlf" + case _ => "auto" + } + } + + def getUseSoftTabs(props: ResourceProperties): Boolean = { + props.getValue[IndentStyleValue](PropertyType.indent_style, IndentStyleValue.tab, false) match { + case IndentStyleValue.space => true + case IndentStyleValue.tab => false + case _ => false + } + } +} diff --git a/src/main/twirl/gitbucket/core/repo/blob.scala.html b/src/main/twirl/gitbucket/core/repo/blob.scala.html index 292cf0f..fd1af24 100644 --- a/src/main/twirl/gitbucket/core/repo/blob.scala.html +++ b/src/main/twirl/gitbucket/core/repo/blob.scala.html @@ -5,10 +5,16 @@ latestCommit: gitbucket.core.util.JGitUtil.CommitInfo, hasWritePermission: Boolean, isBlame: Boolean, - isLfsFile: Boolean)(implicit context: gitbucket.core.controller.Context) + isLfsFile: Boolean, + tabSize: Int)(implicit context: gitbucket.core.controller.Context) @import gitbucket.core.view.helpers @gitbucket.core.html.main(s"${(repository.name :: pathList).mkString("/")} at ${branch} - ${repository.owner}/${repository.name}", Some(repository)) { @gitbucket.core.html.menu("files", repository){ +