diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index b0cdb71843ae..3900255bba9a 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -130,5 +130,12 @@ class ScalaSettings extends Settings.SettingGroup { "" ) + val projectUrl = StringSetting ( + "-project-url", + "project repository homepage", + "The source repository of your project", + "" + ) + val wikiSyntax = BooleanSetting("-Xwiki-syntax", "Retains the Scala2 behavior of using Wiki Syntax in Scaladoc") } diff --git a/doc-tool/resources/_includes/sidebar.html b/doc-tool/resources/_includes/sidebar.html index 3ebfd44232c9..aa1a6fe0440b 100644 --- a/doc-tool/resources/_includes/sidebar.html +++ b/doc-tool/resources/_includes/sidebar.html @@ -11,6 +11,20 @@
  • API
  • +
  • + + +
  • {% endif %} {% for pkg in docs %}
  • @@ -20,7 +34,16 @@

    7 %} +
  • + expand rest
  • +
  • + {% elsif forloop.index > 6 and forloop.length > 7 %} +
  • + {% else %}
  • + {% endif %} +
  • + + {% if forloop.last and forloop.index > 6 %} +
  • - collapse members
  • {% endif %} + + {% endif %} + {% endfor %}

  • {% endfor %} + diff --git a/doc-tool/resources/_includes/toolbar.html b/doc-tool/resources/_includes/toolbar.html index 4625e51508ed..cf19bbbab0c5 100644 --- a/doc-tool/resources/_includes/toolbar.html +++ b/doc-tool/resources/_includes/toolbar.html @@ -6,4 +6,15 @@

    {{ site.project }} Documentation

    {{ site.version }}

    + + {% if site.projectUrl %} + + {% if site.projectUrl contains "github" %} + + {% else %} + + {% endif %} + + {% endif %} +
    diff --git a/doc-tool/resources/_layouts/main.html b/doc-tool/resources/_layouts/main.html index 01e32f4260a5..364da6fb7ade 100644 --- a/doc-tool/resources/_layouts/main.html +++ b/doc-tool/resources/_layouts/main.html @@ -19,7 +19,7 @@ " -sed -i '.original' "1s/^/$currDateComment\\ -/" main.html diff --git a/doc-tool/resources/_layouts/search.html b/doc-tool/resources/_layouts/search.html new file mode 100644 index 000000000000..e8603cbdfc2a --- /dev/null +++ b/doc-tool/resources/_layouts/search.html @@ -0,0 +1,161 @@ +--- +layout: main +extraCSS: + - css/toolbar.css + - css/search.css +--- + +{% include "toolbar" %} + +
    + + Back + +
    + +
    +

    +
    +
    +

    Entity Results

    +
      +
    +
    +
    +

    Member Results

    +
      +
    +
    +
    +
    + + diff --git a/doc-tool/resources/css/api-page.css b/doc-tool/resources/css/api-page.css index 3c24ef281049..bb5b1d180589 100644 --- a/doc-tool/resources/css/api-page.css +++ b/doc-tool/resources/css/api-page.css @@ -69,7 +69,9 @@ h1.section { h1.section > span.expand-button.visible { color: rgba(167, 161, 161, 0.5); font-family: "Source Code Pro", sans-serif; - font-size: 25px; + font-size: 16px; + float: left; + margin: 14px 9px 0 0; } h1.section > span.expand-button.visible:hover { diff --git a/doc-tool/resources/css/dottydoc.css b/doc-tool/resources/css/dottydoc.css index a3e33005bc18..42864102dd04 100644 --- a/doc-tool/resources/css/dottydoc.css +++ b/doc-tool/resources/css/dottydoc.css @@ -18,7 +18,6 @@ body { div#content-wrapper { min-height: 100vh; padding-left: 0; - margin-top: 75px; } div#content-wrapper div#content-body { diff --git a/doc-tool/resources/css/search.css b/doc-tool/resources/css/search.css new file mode 100644 index 000000000000..7169bb19668b --- /dev/null +++ b/doc-tool/resources/css/search.css @@ -0,0 +1,160 @@ +@import url(https://fonts.googleapis.com/css?family=Titillium+Web:200,300,400,600); +@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,300,400,600,700); + +div#back-container { + height: 75px; + width: 100%; + padding-top: 25px; +} + +div#back-container > span { + padding: 10px; + border-left: 1px solid #fff; + margin-left: 27px; + font-family: "Titillium Web", sans-serif; + font-weight: 200; + text-transform: uppercase; + font-size: 13px; +} + +div#back-container > span:hover { + border-left: 1px solid #0fa00f; +} + +div#back-container > span > a#back-anchor:hover, +div#back-container > span > a#back-anchor:focus, +div#back-container > span > a#back-anchor { + color: #373a3c; + text-decoration: none; +} + +div#search-container { + margin: 0 auto; + width: 1000px; +} + +div#search-container > h1#searching-for { + text-align: center; + margin-bottom: 50px; + font-family: "Source Sans Pro", sans-serif; +} + +div#search-container > div#result-container { + width: 100%; +} + +div#result-container > div.results { + float: left; + width: 50%; + padding-right: 20px; + min-height: 40px; /** debug */ +} + +div#entity-results { + background-color: green; /** debug */ +} + +div#member-results { + background-color: red; /** debug */ +} + +div.results > h1 { + width: 100%; + border-bottom: 1px solid black; + font-family: "Titillium Web", sans-serif; + font-weight: 300; + font-size: 24px; +} + +div.results ul { + list-style-type: none; + padding-left: 0; +} + +div.results > ul > li > h1.package-name { + font-size: 13px; + margin-top: 20px; +} + +div.results > ul > li > h1.package-name > a { + font-family: "Titillium Web", sans-serif; + font-weight: 300; + color: #ff4081; +} + +div.results > ul > li a:hover, +div.results > ul > li a:focus, +div.results > ul > li a { + color: #373a3c; + text-decoration: none; +} + +div.results > ul > li > ul { + border-left: 2px solid gray; + font-family: "Titillium Web", sans-serif; + font-size: 13px; + font-weight: 100; +} + +div.results > ul > li > ul > li { + line-height: 40px; +} + +div.results li.entity-result-li { +} + +div.results ul.entity-ul { + position: relative; +} + +div.cover-block { + position: absolute; + background: #fff; + border-right: 2px solid gray; + width: 82px; + height: 100%; + left: -82px; + top: 0; +} + +div.results li.entity-result-li > a.entity-name { + margin-left: 10px; +} + +div.results li.entity-result-li { + transition: all 0.2s ease; + margin-left: -42px; +} + +div.results li.entity-result-li.with-companion { + margin-left: -82px; +} + +div.results li.entity-result-li:hover { + margin-left: 0; +} + +div.results li.entity-result-li > div.entity-kinds { + float: left; +} + +div.results li.entity-result-li > div.entity-kinds > a.letter-anchor { + height: 40px; + width: 40px; + display: block; + float: left; + text-align: center; + color: #fff; +} + +div.results li.entity-result-li > div.entity-kinds > a.letter-anchor.object { + background-color: rgb(44, 108, 141); +} + +div.results li.entity-result-li > div.entity-kinds > a.letter-anchor.class { + background-color: rgb(68, 173, 125); +} + +div.results li.entity-result-li > div.entity-kinds > a.letter-anchor.trait { + background-color: rgb(25, 170, 207); +} diff --git a/doc-tool/resources/css/sidebar.css b/doc-tool/resources/css/sidebar.css index 16566a767c75..09c12352a8ab 100644 --- a/doc-tool/resources/css/sidebar.css +++ b/doc-tool/resources/css/sidebar.css @@ -10,6 +10,7 @@ div#content-wrapper div.index-wrapper { font-family: "Titillium Web", sans-serif; font-weight: 200; padding-left: 14px; + padding-bottom: 75px; } /** Mobile (x < 576px) sidebar: Defaults closed with 60% wide sidebar */ @@ -24,6 +25,14 @@ ul.index-entities { padding-left: 0; } +ul.index-entities > li > input#search-api-input { + border: 0; + margin-left: 22px; + outline: none; + line-height: 35px; + box-shadow: inset 0 0px 0 white, inset 0 -0.5px 0 rgba(0, 0, 0, 0.5); +} + ul.toc { list-style-type: none; padding-top: 18px; @@ -133,6 +142,30 @@ li.entity-package > ul.package-entities > li { transition: all 0.2s ease; } +li.entity-package > ul.package-entities > li#hide-hidden-by-default, +li.entity-package > ul.package-entities > li#show-hidden-by-default { + margin-left: 0; + color: #ff4081; + font-size: 13px; +} + +li.entity-package > ul.package-entities > li#hide-hidden-by-default:hover, +li.entity-package > ul.package-entities > li#show-hidden-by-default:hover { + cursor: pointer; +} + +li.entity-package > ul.package-entities > li#show-hidden-by-default, +li.entity-package > ul.package-entities > li#hide-hidden-by-default.toggled, +li.entity-package > ul.package-entities > li.hidden-by-default.toggled { + display: block; +} + +li.entity-package > ul.package-entities > li.hidden-by-default, +li.entity-package > ul.package-entities > li#show-hidden-by-default.toggled, +li.entity-package > ul.package-entities > li#hide-hidden-by-default { + display: none; +} + li.entity-package > ul.package-entities > li.with-companion { margin-left: -92px; } @@ -187,11 +220,11 @@ ul.package-entities > li > div.entity-kinds > a.letter-anchor.trait { /** Cover block used to hide where the kind blocks come from */ div#cover-block { position: absolute; - background: #fafafa; + background: #fff; border-right: 2px solid gray; - width: 40px; + width: 41px; height: 100%; - left: -40px; + left: -41px; top: 0; } diff --git a/doc-tool/resources/css/toolbar.css b/doc-tool/resources/css/toolbar.css index 5327bf890411..61637e46d075 100644 --- a/doc-tool/resources/css/toolbar.css +++ b/doc-tool/resources/css/toolbar.css @@ -9,6 +9,11 @@ div#toolbar { z-index: 2; } +div#toolbar-spacing { + width: 100%; + height: 75px; +} + div#toolbar > a#home-button svg g#logo-foreground { fill: rgba(202, 68, 94, 1); } @@ -54,3 +59,10 @@ div#toolbar > div#project-details > h2#project-version { margin-left: 1px; } +div#toolbar > a#github-link { + color: #fff; + position: absolute; + top: 7px; + right: 15px; + font-size: 40px; +} diff --git a/doc-tool/resources/js/api-search.js b/doc-tool/resources/js/api-search.js new file mode 100644 index 000000000000..c64d56484466 --- /dev/null +++ b/doc-tool/resources/js/api-search.js @@ -0,0 +1,85 @@ +/** This Webworker performs search on the API structure + * + * It can be used as follows: + * + * ```javascript + * var apiSearch = new Worker(""); + * apiSearch.postMessage({ + * "type": "setup", + * "search": "", + * "docs": + * }); + * ``` + * + * It posts a few different messages to its parent: + * + * ```json + * { + * "type": "entityResult", + * "package": , + * "entity": + * } + * + * { + * "type": "memberResult", + * "package": , + * "parent": , + * "member": + * } + * ``` + */ +onmessage = function(e) { + var docs = e.data.docs; + var searchTerm = e.data.search; + + var regexForTerm = function(query) { + var escaped = query.replace(/([\.\*\+\?\|\(\)\[\]\\])/g, '\\$1'); + if (query.toLowerCase() != query) { + // Regexp that matches CamelCase subbits: "BiSe" is + // "[a-z]*Bi[a-z]*Se" and matches "BitSet", "ABitSet", ... + return new RegExp(escaped.replace(/([A-Z])/g,"[a-z]*$1")); + } + else { // if query is all lower case make a normal case insensitive search + return new RegExp(escaped, "i"); + } + }; + + var searchRegex = regexForTerm(searchTerm); + + var filterPackages = function(entity) { + return entity.kind != "package"; + }; + + // look at this higher order function, such syntax: + var messageParentIfMatches = function(parent) { + return function(entity) { + var fullName = entity.path.join('.'); + + if (searchRegex.test(fullName)) { + postMessage({ + "type": "entityResult", + "package": parent, + "entity": entity + }); + } + + var searchChild = function(member) { + if (searchRegex.test(member.name)) { + postMessage({ + "type": "memberResult", + "package": parent, + "parent": entity, + "member": member, + }); + } + }; + entity.members.forEach(searchChild); + }; + }; + + docs.forEach(function(pack) { + pack.children + .filter(filterPackages) + .forEach(messageParentIfMatches(pack)); + }); +} diff --git a/doc-tool/src/dotty/tools/dottydoc/DocDriver.scala b/doc-tool/src/dotty/tools/dottydoc/DocDriver.scala index 9e26e8da68e4..c9908c96163e 100644 --- a/doc-tool/src/dotty/tools/dottydoc/DocDriver.scala +++ b/doc-tool/src/dotty/tools/dottydoc/DocDriver.scala @@ -46,11 +46,12 @@ class DocDriver extends Driver { val siteRoot = new java.io.File(ctx.settings.siteRoot.value) val projectName = ctx.settings.projectName.value val projectVersion = ctx.settings.projectVersion.value + val projectUrl = ctx.settings.projectUrl.value if (!siteRoot.exists || !siteRoot.isDirectory) ctx.error(s"Site root does not exist: $siteRoot") else { - Site(siteRoot, projectName, projectVersion, ctx.docbase.packages) + Site(siteRoot, projectName, projectVersion, projectUrl, ctx.docbase.packages) .generateApiDocs() .copyStaticFiles() .generateHtmlFiles() diff --git a/doc-tool/src/dotty/tools/dottydoc/staticsite/DefaultParams.scala b/doc-tool/src/dotty/tools/dottydoc/staticsite/DefaultParams.scala index 4eb1fba1459b..300e1b90a724 100644 --- a/doc-tool/src/dotty/tools/dottydoc/staticsite/DefaultParams.scala +++ b/doc-tool/src/dotty/tools/dottydoc/staticsite/DefaultParams.scala @@ -35,7 +35,8 @@ case class DefaultParams( "baseurl" -> site.baseurl, "posts" -> site.posts.map(_.toMap), "project" -> site.projectTitle, - "version" -> site.projectVersion + "version" -> site.projectVersion, + "projectUrl" -> site.projectUrl ).asJava, "sidebar" -> sidebar.titles.asJava @@ -50,7 +51,8 @@ case class DefaultParams( } def withPosts(posts: Array[BlogPost]): DefaultParams = - copy(site = SiteInfo(site.baseurl, site.projectTitle, site.projectVersion, posts)) + copy(site = SiteInfo( + site.baseurl, site.projectTitle, site.projectVersion, site.projectUrl, posts)) def withUrl(url: String): DefaultParams = copy(page = PageInfo(url)) @@ -68,6 +70,7 @@ case class SiteInfo( baseurl: String, projectTitle: String, projectVersion: String, + projectUrl: String, posts: Array[BlogPost] ) diff --git a/doc-tool/src/dotty/tools/dottydoc/staticsite/Site.scala b/doc-tool/src/dotty/tools/dottydoc/staticsite/Site.scala index dc3a4c0b825a..4a18b2e12255 100644 --- a/doc-tool/src/dotty/tools/dottydoc/staticsite/Site.scala +++ b/doc-tool/src/dotty/tools/dottydoc/staticsite/Site.scala @@ -28,7 +28,13 @@ import io.{ AbstractFile, VirtualFile, File } import scala.collection.mutable.ArrayBuffer import util.syntax._ -case class Site(val root: JFile, val projectTitle: String, val projectVersion: String, val documentation: Map[String, Package]) extends ResourceFinder { +case class Site( + val root: JFile, + val projectTitle: String, + val projectVersion: String, + val projectUrl: String, + val documentation: Map[String, Package] +) extends ResourceFinder { /** Documentation serialized to java maps */ private val docs: JList[_] = { import model.JavaConverters._ @@ -128,12 +134,14 @@ case class Site(val root: JFile, val projectTitle: String, val projectVersion: S // Copy statics included in resources Map( "css/toolbar.css" -> "/css/toolbar.css", + "css/search.css" -> "/css/search.css", "css/sidebar.css" -> "/css/sidebar.css", "css/api-page.css" -> "/css/api-page.css", "css/dottydoc.css" -> "/css/dottydoc.css", "css/color-brewer.css" -> "/css/color-brewer.css", "css/font-awesome.min.css" -> "/css/font-awesome.min.css", "css/bootstrap.min.css" -> "/css/bootstrap.min.css", + "js/api-search.js" -> "/js/api-search.js", "js/bootstrap.min.js" -> "/js/bootstrap.min.js", "js/jquery.min.js" -> "/js/jquery.min.js", "js/tether.min.js" -> "/js/tether.min.js", @@ -157,7 +165,11 @@ case class Site(val root: JFile, val projectTitle: String, val projectVersion: S "../" * (assetLen - rootLen - 1 + additionalDepth) + "." } - DefaultParams(docs, documentation, PageInfo(pathFromRoot), SiteInfo(baseUrl, projectTitle, projectVersion, Array()), sidebar) + DefaultParams( + docs, documentation, PageInfo(pathFromRoot), + SiteInfo(baseUrl, projectTitle, projectVersion, projectUrl, Array()), + sidebar + ) } /* Creates output directories if allowed */ @@ -196,6 +208,18 @@ case class Site(val root: JFile, val projectTitle: String, val projectVersion: S genDoc(pkg) pkg.children.foreach(genDoc) } + + // generate search page: + val target = mkdirs(fs.getPath(outDir.getAbsolutePath + "/api/search.html")) + val searchPageParams = defaultParams(target.toFile, -1).withPosts(blogInfo).toMap + val searchPage = new HtmlPage("_layouts/search.html", layouts("search").content, searchPageParams, includes) + render(searchPage).foreach { rendered => + Files.copy( + new ByteArrayInputStream(rendered.getBytes(StandardCharsets.UTF_8)), + target, + REPLACE_EXISTING + ) + } } /** Generate HTML files from markdown and .html sources */ @@ -328,6 +352,7 @@ case class Site(val root: JFile, val projectTitle: String, val projectVersion: S val defaultLayouts: Map[String, Layout] = Map( "main" -> "/_layouts/main.html", + "search" -> "/_layouts/search.html", "doc-page" -> "/_layouts/doc-page.html", "api-page" -> "/_layouts/api-page.html", "blog-page" -> "/_layouts/blog-page.html", diff --git a/doc-tool/src/dotty/tools/dottydoc/staticsite/Template.scala b/doc-tool/src/dotty/tools/dottydoc/staticsite/Template.scala index 498b21f57c0c..75350cffd4d9 100644 --- a/doc-tool/src/dotty/tools/dottydoc/staticsite/Template.scala +++ b/doc-tool/src/dotty/tools/dottydoc/staticsite/Template.scala @@ -35,6 +35,7 @@ case class LiquidTemplate(path: String, content: SourceFile) extends Template wi /** Register filters to static container */ Filter.registerFilter(new Reverse) Filter.registerFilter(new First) + Filter.registerFilter(new Json) // For some reason, liqp rejects a straight conversion using `.asJava` private def toJavaMap(map: Map[String, AnyRef]): HashMap[String, Object] = diff --git a/doc-tool/src/dotty/tools/dottydoc/staticsite/filters.scala b/doc-tool/src/dotty/tools/dottydoc/staticsite/filters.scala index 1569d38737cc..718ba6b37e8c 100644 --- a/doc-tool/src/dotty/tools/dottydoc/staticsite/filters.scala +++ b/doc-tool/src/dotty/tools/dottydoc/staticsite/filters.scala @@ -2,8 +2,9 @@ package dotty.tools package dottydoc package staticsite -import java.util.{ Map => JMap } +import java.util.{ Map => JMap, List => JList } +import com.fasterxml.jackson.databind.ObjectMapper import liqp.filters.Filter /** Custom liquid template filters */ @@ -39,4 +40,20 @@ object filters { case _ => null } } + + /** Used to transform java representation to valid JSON + * + * This can be used on things like the docs (`java.util.List[Map[String,_]]`) + * which are available globally: + * + * ```html + * {{ docs | json }} + * ``` + */ + final class Json extends Filter("json") { + override def apply(value: Any, params: AnyRef*): AnyRef = value match { + case map: JList[_] => new ObjectMapper().writeValueAsString(map) + case _ => null + } + } } diff --git a/doc-tool/test/SourceFileOps.scala b/doc-tool/test/SourceFileOps.scala index 81ea4eaf03a9..d1bb93e0a75e 100644 --- a/doc-tool/test/SourceFileOps.scala +++ b/doc-tool/test/SourceFileOps.scala @@ -11,7 +11,10 @@ import model.Package trait SourceFileOps { import scala.collection.JavaConverters._ - val site = new Site(new java.io.File("../doc-tool/resources/"), "test-site", "v0.1", Map.empty) + val site = new Site( + new java.io.File("../doc-tool/resources/"), + "test-site", "v0.1", "http://github.com/lampepfl/dotty", Map.empty + ) def stringToSource(path: String, sourceCode: String): SourceFile = { val virtualFile = new VirtualFile(path, path) diff --git a/doc-tool/test/dotty/tools/dottydoc/staticsite/SiteTests.scala b/doc-tool/test/dotty/tools/dottydoc/staticsite/SiteTests.scala index f0e5538dcfba..db54c94cb3e0 100644 --- a/doc-tool/test/dotty/tools/dottydoc/staticsite/SiteTests.scala +++ b/doc-tool/test/dotty/tools/dottydoc/staticsite/SiteTests.scala @@ -10,7 +10,7 @@ class SiteTests extends DottyDocTest with SourceFileOps { assert(site.root.exists && site.root.isDirectory, s"'${site.root.getName}' is not a directory") - val expectedLayouts = Set("main", "index", "blog-page", "doc-page", "api-page") + val expectedLayouts = Set("main", "index", "blog-page", "doc-page", "api-page", "search") assert(site.layouts.keys == expectedLayouts, s"Incorrect layouts in: ${site.layouts.keys}, expected: $expectedLayouts") } @@ -84,11 +84,13 @@ class SiteTests extends DottyDocTest with SourceFileOps { val expectedAssets = Set( "css/toolbar.css", "css/sidebar.css", + "css/search.css", "css/api-page.css", "css/dottydoc.css", "css/color-brewer.css", "css/bootstrap.min.css", "css/font-awesome.min.css", + "js/api-search.js", "js/highlight.pack.js", "js/bootstrap.min.js", "js/tether.min.js", @@ -99,9 +101,12 @@ class SiteTests extends DottyDocTest with SourceFileOps { "index.md" ) + def printSet(xs: Set[String]): String = + xs.toList.sorted.mkString("\n{\n ", ",\n ", "\n}") + assert(expectedAssets == assets, - s"assets incorrect, found: $assets - expected $expectedAssets") + s"assets incorrect, found: ${ printSet(assets) } - expected ${ printSet(expectedAssets) }") assert(expectedCompd == compd, - s"compilable files incorrect, found: $compd - expected $expectedCompd") + s"compilable files incorrect, found: ${ printSet(compd) } - expected ${ printSet(expectedCompd) }") } } diff --git a/project/Build.scala b/project/Build.scala index f209fb2d0735..bb852131a3ab 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -27,6 +27,7 @@ object Build { val scalacVersion = "2.11.11" // Do not rename, this is grepped in bin/common. val dottyOrganization = "ch.epfl.lamp" + val dottyGithubUrl = "https://github.com/lampepfl/dotty" val dottyVersion = { val baseVersion = "0.1.1" val isNightly = sys.env.get("NIGHTLYBUILD") == Some("yes") @@ -86,7 +87,7 @@ object Build { organization := dottyOrganization, organizationName := "LAMP/EPFL", organizationHomepage := Some(url("http://lamp.epfl.ch")), - homepage := Some(url("https://github.com/lampepfl/dotty")), + homepage := Some(url(dottyGithubUrl)), scalacOptions ++= Seq( "-feature", @@ -283,6 +284,7 @@ object Build { "-siteroot", "docs", "-project", "Dotty", "-project-version", dottyVersion, + "-project-url", dottyGithubUrl, "-classpath", s"$dottyLib:$dottyInterfaces:$otherDeps" ) (runMain in Compile).toTask( @@ -887,12 +889,12 @@ object DottyInjectedPlugin extends AutoPlugin { Some("releases" at nexus + "service/local/staging/deploy/maven2") }, publishArtifact in Test := false, - homepage := Some(url("https://github.com/lampepfl/dotty")), + homepage := Some(url(dottyGithubUrl)), licenses += ("BSD New", - url("https://github.com/lampepfl/dotty/blob/master/LICENSE.md")), + url(s"$dottyGithubUrl/blob/master/LICENSE.md")), scmInfo := Some( ScmInfo( - url("https://github.com/lampepfl/dotty"), + url(dottyGithubUrl), "scm:git:git@github.com:lampepfl/dotty.git" ) ),