From e350126794f5836ee0b4fe9c8d31254b3687945f Mon Sep 17 00:00:00 2001 From: KOUNOIKE Yuusuke Date: Sat, 21 Apr 2018 10:33:29 +0900 Subject: [PATCH 1/4] Support EditorConfig for online browser/editor. --- build.sbt | 3 +- src/main/java/editorconfig/JGitResource.java | 134 ++++++++++++++++++ .../java/editorconfig/JGitResourcePath.java | 84 +++++++++++ .../RepositoryViewerController.scala | 36 +++-- .../core/util/EditorConfigUtil.scala | 46 ++++++ .../twirl/gitbucket/core/repo/blob.scala.html | 8 +- .../gitbucket/core/repo/editor.scala.html | 9 +- .../core/util/EditorConfigUtilSpec.scala | 38 +++++ 8 files changed, 342 insertions(+), 16 deletions(-) create mode 100644 src/main/java/editorconfig/JGitResource.java create mode 100644 src/main/java/editorconfig/JGitResourcePath.java create mode 100644 src/main/scala/gitbucket/core/util/EditorConfigUtil.scala create mode 100644 src/test/scala/gitbucket/core/util/EditorConfigUtilSpec.scala diff --git a/build.sbt b/build.sbt index 10723d9f2..26b441454 100644 --- a/build.sbt +++ b/build.sbt @@ -68,7 +68,8 @@ libraryDependencies ++= Seq( "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 000000000..5590d9fd6 --- /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 000000000..8d99d1e0b --- /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 3da3594f9..81d4e8644 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 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 @@ trait RepositoryViewerControllerBase extends ControllerBase { 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 @@ trait RepositoryViewerControllerBase extends ControllerBase { // 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 @@ trait RepositoryViewerControllerBase extends ControllerBase { 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 000000000..edc13d86a --- /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 292cf0fc5..fd1af24ee 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){ +
Transfer to URL with SHA diff --git a/src/main/twirl/gitbucket/core/repo/editor.scala.html b/src/main/twirl/gitbucket/core/repo/editor.scala.html index 9b984fe64..743530a57 100644 --- a/src/main/twirl/gitbucket/core/repo/editor.scala.html +++ b/src/main/twirl/gitbucket/core/repo/editor.scala.html @@ -4,7 +4,11 @@ fileName: Option[String], content: gitbucket.core.util.JGitUtil.ContentInfo, protectedBranch: Boolean, - commit: String)(implicit context: gitbucket.core.controller.Context) + commit: String, + newLineMode: String = "auto", + useSoftTabs: Boolean = false, + tabSize: Int = 8 +)(implicit context: gitbucket.core.controller.Context) @import gitbucket.core.view.helpers @gitbucket.core.html.main(if(fileName.isEmpty) "New File" else s"Editing ${fileName.get} at ${branch} - ${repository.owner}/${repository.name}", Some(repository)) { @gitbucket.core.html.menu("files", repository){ @@ -94,6 +98,9 @@ $(function(){ @if(protectedBranch){ editor.setReadOnly(true); } + editor.getSession().setOption("tabSize", @tabSize); + editor.getSession().setOption("newLineMode", "@newLineMode"); + editor.getSession().setOption("useSoftTabs", @useSoftTabs); editor.on('change', function(){ updateCommitButtonStatus(); diff --git a/src/test/scala/gitbucket/core/util/EditorConfigUtilSpec.scala b/src/test/scala/gitbucket/core/util/EditorConfigUtilSpec.scala new file mode 100644 index 000000000..b0bf60b02 --- /dev/null +++ b/src/test/scala/gitbucket/core/util/EditorConfigUtilSpec.scala @@ -0,0 +1,38 @@ +package gitbucket.core.util + +import org.scalatest.FunSuite +import GitSpecUtil._ + +class EditorConfigUtilSpec extends FunSuite { + val simpleConfig = + """[*.txt] + |indent_style = tab + |indent_size = 4""".stripMargin + + test("no EditorConfig file") { + withTestRepository { git => + createFile(git, "master", "README.md", "body", message = "commit1") + val props = EditorConfigUtil.readProperties(git, "master", "test.txt") + assert(EditorConfigUtil.getTabWidth(props) == 8) + assert(EditorConfigUtil.getUseSoftTabs(props) == false) + assert(EditorConfigUtil.getNewLineMode(props) == "auto") + + val subdirProps = EditorConfigUtil.readProperties(git, "master", "dir1/dir2/dir3/dir4/test.txt") + assert(EditorConfigUtil.getTabWidth(subdirProps) == 8) + assert(EditorConfigUtil.getUseSoftTabs(subdirProps) == false) + assert(EditorConfigUtil.getNewLineMode(subdirProps) == "auto") + } + } + + test("simple EditorConfig") { + withTestRepository { git => + createFile(git, "master", ".editorconfig", simpleConfig, message = "commit1") + + val props = EditorConfigUtil.readProperties(git, "master", "test.txt") + assert(EditorConfigUtil.getTabWidth(props) == 4) + + val subdirProps = EditorConfigUtil.readProperties(git, "master", "dir1/dir2/dir3/dir4/test.txt") + assert(EditorConfigUtil.getTabWidth(subdirProps) == 4) + } + } +} From e2d53827872ada0c1c729dabe3b21fab64817bc6 Mon Sep 17 00:00:00 2001 From: KOUNOIKE Yuusuke Date: Sun, 22 Apr 2018 12:42:50 +0900 Subject: [PATCH 2/4] scalafmtSbt applied --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 26b441454..a2c60125b 100644 --- a/build.sbt +++ b/build.sbt @@ -69,7 +69,7 @@ libraryDependencies ++= Seq( "ru.yandex.qatools.embed" % "postgresql-embedded" % "2.6" % "test", "net.i2p.crypto" % "eddsa" % "0.2.0", "is.tagomor.woothee" % "woothee-java" % "1.7.0", - "org.ec4j.core" % "ec4j-core" % "0.0.1" + "org.ec4j.core" % "ec4j-core" % "0.0.1" ) // Compiler settings From 58381c3d3094d9537478b27ffc92a71501a2708c Mon Sep 17 00:00:00 2001 From: KOUNOIKE Yuusuke Date: Sun, 22 Apr 2018 16:46:34 +0900 Subject: [PATCH 3/4] Handle .editorconfig parse error --- .../RepositoryViewerController.scala | 13 ++-- .../core/util/EditorConfigUtil.scala | 67 ++++++++++--------- .../core/util/EditorConfigUtilSpec.scala | 35 ++++++---- 3 files changed, 66 insertions(+), 49 deletions(-) diff --git a/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala b/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala index 81d4e8644..9a4a71326 100644 --- a/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala +++ b/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala @@ -318,7 +318,7 @@ trait RepositoryViewerControllerBase extends ControllerBase { getPathObjectId(git, path, revCommit).map { objectId => val paths = path.split("/") - val props = EditorConfigUtil.readProperties(git, branch, path) + val info = EditorConfigUtil.getEditorConfigInfo(git, branch, path) html.editor( branch = branch, @@ -328,9 +328,9 @@ trait RepositoryViewerControllerBase extends ControllerBase { content = JGitUtil.getContentInfo(git, path, objectId), protectedBranch = protectedBranch, commit = revCommit.getName, - newLineMode = EditorConfigUtil.getNewLineMode(props), - useSoftTabs = EditorConfigUtil.getUseSoftTabs(props), - tabSize = EditorConfigUtil.getTabWidth(props) + newLineMode = info.newLineMode, + useSoftTabs = info.useSoftTabs, + tabSize = info.tabSize ) } getOrElse NotFound() } @@ -442,8 +442,7 @@ trait RepositoryViewerControllerBase extends ControllerBase { // 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) + val info = EditorConfigUtil.getEditorConfigInfo(git, id, path) html.blob( branch = id, repository = repository, @@ -453,7 +452,7 @@ trait RepositoryViewerControllerBase extends ControllerBase { hasWritePermission = hasDeveloperRole(repository.owner, repository.name, context.loginAccount), isBlame = request.paths(2) == "blame", isLfsFile = isLfsFile(git, objectId), - tabSize = tabSize + tabSize = info.tabSize ) } } getOrElse NotFound() diff --git a/src/main/scala/gitbucket/core/util/EditorConfigUtil.scala b/src/main/scala/gitbucket/core/util/EditorConfigUtil.scala index edc13d86a..3d9bac555 100644 --- a/src/main/scala/gitbucket/core/util/EditorConfigUtil.scala +++ b/src/main/scala/gitbucket/core/util/EditorConfigUtil.scala @@ -1,46 +1,53 @@ package gitbucket.core.util -import java.nio.charset.StandardCharsets +import java.io.IOException import editorconfig.{JGitResource, JGitResourcePath} import org.ec4j.core.model.PropertyType.{EndOfLineValue, IndentStyleValue} import org.ec4j.core.model.{PropertyType, Version} +import org.ec4j.core.parser.ParseException 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() + val TabSizeDefault: Int = 8 + val NewLineModeDefault: String = "auto" + val UseSoftTabsDefault = false - resourcePropertiesService.queryProperties(new JGitResource(git, rev, path)) - } + case class EditorConfigInfo( + tabSize: Int, + newLineMode: String, + useSoftTabs: Boolean + ) - def getTabWidth(props: ResourceProperties): Int = { - props.getValue[Integer](PropertyType.tab_width, 8, false) - } + def getEditorConfigInfo(git: Git, rev: String, path: String): EditorConfigInfo = { + try { + val resourcePropertiesService = ResourcePropertiesService + .builder() + .configFileName(EditorConfigConstants.EDITORCONFIG) + .rootDirectory(JGitResourcePath.RootDirectory(git, rev)) + .loader(EditorConfigLoader.of(Version.CURRENT)) + .keepUnset(true) + .build() - 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 + val props = resourcePropertiesService.queryProperties(new JGitResource(git, rev, path)) + EditorConfigInfo( + tabSize = props.getValue[Integer](PropertyType.tab_width, TabSizeDefault, false), + newLineMode = props.getValue[EndOfLineValue](PropertyType.end_of_line, null, false) match { + case EndOfLineValue.cr => "cr" + case EndOfLineValue.lf => "lf" + case EndOfLineValue.crlf => "crlf" + case _ => "auto" + }, + props.getValue[IndentStyleValue](PropertyType.indent_style, null, false) match { + case IndentStyleValue.space => true + case IndentStyleValue.tab => false + case _ => false + } + ) + } catch { + case e: ParseException => EditorConfigInfo(TabSizeDefault, NewLineModeDefault, UseSoftTabsDefault) + case e: IOException => EditorConfigInfo(TabSizeDefault, NewLineModeDefault, UseSoftTabsDefault) } } } diff --git a/src/test/scala/gitbucket/core/util/EditorConfigUtilSpec.scala b/src/test/scala/gitbucket/core/util/EditorConfigUtilSpec.scala index b0bf60b02..791a79761 100644 --- a/src/test/scala/gitbucket/core/util/EditorConfigUtilSpec.scala +++ b/src/test/scala/gitbucket/core/util/EditorConfigUtilSpec.scala @@ -12,15 +12,15 @@ class EditorConfigUtilSpec extends FunSuite { test("no EditorConfig file") { withTestRepository { git => createFile(git, "master", "README.md", "body", message = "commit1") - val props = EditorConfigUtil.readProperties(git, "master", "test.txt") - assert(EditorConfigUtil.getTabWidth(props) == 8) - assert(EditorConfigUtil.getUseSoftTabs(props) == false) - assert(EditorConfigUtil.getNewLineMode(props) == "auto") + val info = EditorConfigUtil.getEditorConfigInfo(git, "master", "test.txt") + assert(info.tabSize == 8) + assert(info.useSoftTabs == false) + assert(info.newLineMode == "auto") - val subdirProps = EditorConfigUtil.readProperties(git, "master", "dir1/dir2/dir3/dir4/test.txt") - assert(EditorConfigUtil.getTabWidth(subdirProps) == 8) - assert(EditorConfigUtil.getUseSoftTabs(subdirProps) == false) - assert(EditorConfigUtil.getNewLineMode(subdirProps) == "auto") + val subdirInfo = EditorConfigUtil.getEditorConfigInfo(git, "master", "dir1/dir2/dir3/dir4/test.txt") + assert(subdirInfo.tabSize == 8) + assert(subdirInfo.useSoftTabs == false) + assert(subdirInfo.newLineMode == "auto") } } @@ -28,11 +28,22 @@ class EditorConfigUtilSpec extends FunSuite { withTestRepository { git => createFile(git, "master", ".editorconfig", simpleConfig, message = "commit1") - val props = EditorConfigUtil.readProperties(git, "master", "test.txt") - assert(EditorConfigUtil.getTabWidth(props) == 4) + val info = EditorConfigUtil.getEditorConfigInfo(git, "master", "test.txt") + assert(info.tabSize == 4) - val subdirProps = EditorConfigUtil.readProperties(git, "master", "dir1/dir2/dir3/dir4/test.txt") - assert(EditorConfigUtil.getTabWidth(subdirProps) == 4) + val subdirInfo = EditorConfigUtil.getEditorConfigInfo(git, "master", "dir1/dir2/dir3/dir4/test.txt") + assert(subdirInfo.tabSize == 4) + } + } + + test(".editorconfig parse error") { + withTestRepository { git => + createFile(git, "master", ".editorconfig", "equal_missing", message = "commit1") + + val info = EditorConfigUtil.getEditorConfigInfo(git, "master", "test.txt") + assert(info.tabSize == 8) + assert(info.useSoftTabs == false) + assert(info.newLineMode == "auto") } } } From d5537713358cc14107a413f2218577f6bc950634 Mon Sep 17 00:00:00 2001 From: KOUNOIKE Yuusuke Date: Sun, 1 Jul 2018 13:16:48 +0900 Subject: [PATCH 4/4] implement JGitResource/JGitResourcePath class with scala --- src/main/java/editorconfig/JGitResource.java | 134 ------------------ .../java/editorconfig/JGitResourcePath.java | 84 ----------- .../core/util/EditorConfigUtil.scala | 107 +++++++++++++- 3 files changed, 100 insertions(+), 225 deletions(-) delete mode 100644 src/main/java/editorconfig/JGitResource.java delete mode 100644 src/main/java/editorconfig/JGitResourcePath.java diff --git a/src/main/java/editorconfig/JGitResource.java b/src/main/java/editorconfig/JGitResource.java deleted file mode 100644 index 5590d9fd6..000000000 --- a/src/main/java/editorconfig/JGitResource.java +++ /dev/null @@ -1,134 +0,0 @@ -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 deleted file mode 100644 index 8d99d1e0b..000000000 --- a/src/main/java/editorconfig/JGitResourcePath.java +++ /dev/null @@ -1,84 +0,0 @@ -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/util/EditorConfigUtil.scala b/src/main/scala/gitbucket/core/util/EditorConfigUtil.scala index 3d9bac555..e68efaf62 100644 --- a/src/main/scala/gitbucket/core/util/EditorConfigUtil.scala +++ b/src/main/scala/gitbucket/core/util/EditorConfigUtil.scala @@ -1,18 +1,111 @@ package gitbucket.core.util -import java.io.IOException +import java.io.{IOException, InputStreamReader, Reader} +import java.nio.charset.StandardCharsets -import editorconfig.{JGitResource, JGitResourcePath} +import org.ec4j.core.Resource.Resources.StringRandomReader import org.ec4j.core.model.PropertyType.{EndOfLineValue, IndentStyleValue} -import org.ec4j.core.model.{PropertyType, Version} +import org.ec4j.core.model.{Ec4jPath, PropertyType, Version} import org.ec4j.core.parser.ParseException -import org.ec4j.core.{EditorConfigConstants, EditorConfigLoader, ResourceProperties, ResourcePropertiesService} +import org.ec4j.core._ import org.eclipse.jgit.api.Git +import org.eclipse.jgit.lib.{ObjectReader, Repository} +import org.eclipse.jgit.revwalk.{RevTree, RevWalk} +import org.eclipse.jgit.treewalk.TreeWalk +import gitbucket.core.util.SyntaxSugars._ object EditorConfigUtil { - val TabSizeDefault: Int = 8 - val NewLineModeDefault: String = "auto" - val UseSoftTabsDefault = false + private class JGitResource(repo: Repository, revStr: String, path: Ec4jPath) extends Resource { + private def removeInitialSlash(path: Ec4jPath) = Ec4jPath.Ec4jPaths.root.relativize(path).toString + + def this(git: Git, revStr: String, path: String) = { + this(git.getRepository, revStr, Ec4jPath.Ec4jPaths.of(if (path.startsWith("/")) path else "/" + path)) + } + + def this(repo: Repository, revStr: String, path: String) = { + this(repo, revStr, Ec4jPath.Ec4jPaths.of(if (path.startsWith("/")) path else "/" + path)) + } + + private def getRevTree: RevTree = { + using(repo.newObjectReader()) { reader: ObjectReader => + val revWalk = new RevWalk(reader) + val id = repo.resolve(revStr) + val commit = revWalk.parseCommit(id) + commit.getTree + } + } + + override def exists(): Boolean = { + using(repo.newObjectReader()) { reader: ObjectReader => + try { + val treeWalk = Option(TreeWalk.forPath(reader, removeInitialSlash(path), getRevTree)) + treeWalk.isDefined + } catch { + case e: IOException => false + } + } + } + + override def getPath: Ec4jPath = { + path + } + + override def getParent: ResourcePath = { + Option(path.getParentPath).map { new JGitResourcePath(repo, revStr, _) }.getOrElse(null) + } + + override def openRandomReader(): Resource.RandomReader = { + StringRandomReader.ofReader(openReader()) + } + + override def openReader(): Reader = { + using(repo.newObjectReader) { reader: ObjectReader => + val treeWalk = TreeWalk.forPath(reader, removeInitialSlash(path), getRevTree) + new InputStreamReader(reader.open(treeWalk.getObjectId(0)).openStream, StandardCharsets.UTF_8) + } + } + } + + private class JGitResourcePath(repo: Repository, revStr: String, path: Ec4jPath) extends ResourcePath { + + override def getParent: ResourcePath = { + Option(path.getParentPath).map { new JGitResourcePath(repo, revStr, _) }.getOrElse(null) + } + + override def getPath: Ec4jPath = { + path + } + + override def hasParent: Boolean = { + Option(path.getParentPath).isDefined + } + + override def relativize(resource: Resource): Resource = { + resource match { + case r: JGitResource => + new JGitResource(repo, revStr, path.relativize(r.getPath).toString) + } + } + + override def resolve(name: String): Resource = { + Option(path) + .map { p => + new JGitResource(repo, revStr, p.resolve(name)) + } + .getOrElse { + new JGitResource(repo, revStr, name) + } + } + } + + private object JGitResourcePath { + def RootDirectory(git: Git, revStr: String) = + new JGitResourcePath(git.getRepository, revStr, Ec4jPath.Ec4jPaths.of("/")) + } + + private val TabSizeDefault: Int = 8 + private val NewLineModeDefault: String = "auto" + private val UseSoftTabsDefault = false case class EditorConfigInfo( tabSize: Int,