Skip to content

Commit

Permalink
Insert / in doc-source-url if relative path follows word character
Browse files Browse the repository at this point in the history
  • Loading branch information
lrytz committed Oct 23, 2023
1 parent bf45e19 commit 12db79c
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 20 deletions.
57 changes: 39 additions & 18 deletions src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala
Original file line number Diff line number Diff line change
Expand Up @@ -318,27 +318,21 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {

if (!settings.docsourceurl.isDefault)
inSource.map { case (file, line) =>
// file path is relative to source root (-sourcepath)
val src = Paths.get(settings.sourcepath.value).toUri
val path = file.file.toPath.toUri
val filePathExt = src.relativize(path)
val rawPath: String = filePathExt.getRawPath
val filePathExt = {
// file path is relative to source root (-sourcepath); use an absolute path otherwise
val sp = settings.sourcepath.value
val fileUri = file.file.toPath.toUri
if (sp.isEmpty) fileUri.getRawPath
else Paths.get(sp).toUri.relativize(fileUri).getRawPath
}
val (filePath, fileExt) =
rawPath.lastIndexOf('.') match {
case -1 => (rawPath, "")
case i => rawPath.splitAt(i)
filePathExt.lastIndexOf('.') match {
case -1 => (filePathExt, "")
case i => filePathExt.splitAt(i)
}
val tplOwner = this.inTemplate.qualifiedName
val tplName = this.name
def substitute(name: String): String = name match {
case FILE_PATH => filePath
case FILE_EXT => fileExt
case FILE_PATH_EXT => filePathExt.toString
case FILE_LINE => line.toString
case TPL_OWNER => tplOwner
case TPL_NAME => tplName
}
val patchedString = tokens.replaceAllIn(settings.docsourceurl.value, m => quoteReplacement(substitute(m.group(1))) )
val patchedString = expandUrl(settings.docsourceurl.value, filePath, fileExt, filePathExt, line, tplOwner, tplName)
new URI(patchedString).toURL
}
else None
Expand Down Expand Up @@ -1061,11 +1055,38 @@ object ModelFactory {
val defaultGroupDesc = None
val defaultGroupPriority = 1000

val tokens = raw"€\{($FILE_PATH|$FILE_EXT|$FILE_PATH_EXT|$FILE_LINE|$TPL_OWNER|$TPL_NAME)\}".r
val tokens = raw"(?:(.?)€\{($FILE_PATH|$FILE_PATH_EXT)\}|\.?€\{($FILE_EXT)\}|€\{($FILE_LINE|$TPL_OWNER|$TPL_NAME)\})".r
final val FILE_PATH = "FILE_PATH"
final val FILE_EXT = "FILE_EXT"
final val FILE_PATH_EXT = "FILE_PATH_EXT"
final val FILE_LINE = "FILE_LINE"
final val TPL_OWNER = "TPL_OWNER"
final val TPL_NAME = "TPL_NAME"

val WordChar = raw"\w".r

def expandUrl(urlTemplate: String, filePath: String, fileExt: String, filePathExt: String, line: Int, tplOwner: String, tplName: String): String = {
val absolute = filePath.startsWith("/")

def subst(groups: List[String]): String = {
// If a relative path follows a word character, insert a `/`
def sep: String = groups.head match {
case null => ""
case "/" if absolute => ""
case c@WordChar() if !absolute => c + "/"
case c => c
}

groups.tail.find(_ != null).get match {
case FILE_PATH => s"$sep$filePath"
case FILE_EXT => fileExt
case FILE_PATH_EXT => s"$sep$filePathExt"
case FILE_LINE => line.toString
case TPL_OWNER => tplOwner
case TPL_NAME => tplName
}
}

tokens.replaceAllIn(urlTemplate, m => quoteReplacement(subst(m.subgroups)))
}
}
24 changes: 24 additions & 0 deletions test/junit/scala/tools/nsc/doc/html/ModelFactoryTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package scala.tools.nsc.doc.html

import org.junit.Assert.assertEquals
import org.junit.Test

import scala.tools.nsc.doc.model.ModelFactory._
import scala.util.matching.Regex.quoteReplacement

class ModelFactoryTest {
def t(expected: String, template: String,
filePath: String = null, fileExt: String = null, line: Int = 0, tplOwner: String = null, tplName: String = null) =
assertEquals(expected, expandUrl(template, filePath, fileExt, filePath + fileExt, line, tplOwner, tplName))

@Test def sourceUrlReplace(): Unit = {
t("/base/p/file.scala", "/base€{FILE_PATH_EXT}", filePath = "/p/file", fileExt = ".scala")
t("/base/p/file.scala", "/base/€{FILE_PATH_EXT}", filePath = "/p/file", fileExt = ".scala")

t("/base/p/file.scala", "/base€{FILE_PATH_EXT}", filePath = "p/file", fileExt = ".scala")
t("/base/p/file.scala", "/base/€{FILE_PATH_EXT}", filePath = "p/file", fileExt = ".scala")

t("/base/p/file.scala", "/base/€{FILE_PATH}€{FILE_EXT}", filePath = "/p/file", fileExt = ".scala")
t("/base/p/file.scala", "/base/€{FILE_PATH}.€{FILE_EXT}", filePath = "/p/file", fileExt =".scala")
}
}
2 changes: 1 addition & 1 deletion test/scaladoc/run/doc-source-url-java.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ object Test extends ScaladocModelTest {

override def model: Option[Universe] = newDocFactory.makeUniverse(Left(List(resourceFile)))

def scaladocSettings = "-doc-source-url file:€{FILE_PATH}@@€{FILE_EXT}@@€{FILE_PATH_EXT}@@€{FILE_LINE}"
def scaladocSettings = "-doc-source-url file:€{FILE_PATH}@@€{FILE_EXT}@@€{FILE_PATH_EXT}@@€{FILE_LINE} -sourcepath ."

def testModel(rootPackage: Package) = {
import access._
Expand Down
2 changes: 1 addition & 1 deletion test/scaladoc/run/doc-source-url.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ object Test extends ScaladocModelTest {

override def model: Option[Universe] = newDocFactory.makeUniverse(Left(List(resourceFile)))

def scaladocSettings = "-doc-source-url file:€{FILE_PATH}@@€{FILE_EXT}@@€{FILE_PATH_EXT}@@€{FILE_LINE}"
def scaladocSettings = "-doc-source-url file:€{FILE_PATH}@@€{FILE_EXT}@@€{FILE_PATH_EXT}@@€{FILE_LINE} -sourcepath ."

def testModel(rootPackage: Package) = {
import access._
Expand Down

0 comments on commit 12db79c

Please sign in to comment.