Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancements to ScalaDoc: New Features Implemented #21436

Draft
wants to merge 25 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8fc1f15
Added AltDetails Block
sai-prasad-1 Jun 8, 2024
feac040
Update : liqp version to 0.9
sai-prasad-1 Jun 8, 2024
89d11f9
Added Tabs Block
sai-prasad-1 Jun 12, 2024
b2eab74
Added Include Tag
sai-prasad-1 Jun 26, 2024
5351239
Update Nameing
sai-prasad-1 Jun 26, 2024
5fa510a
Added Include Tag and optimise alt-details
sai-prasad-1 Jul 5, 2024
44e670e
Update styling for alt-details and tabs
sai-prasad-1 Jul 5, 2024
907584b
Fix Front-matter parser
sai-prasad-1 Jul 10, 2024
f25ab16
Update Tabs Block to use Radio Buttons
sai-prasad-1 Jul 25, 2024
c5b5450
Update : Added Langauge Picker
sai-prasad-1 Aug 7, 2024
76247e9
Fix Config Path
sai-prasad-1 Aug 7, 2024
44548bb
Update Config Loader
sai-prasad-1 Aug 11, 2024
b8b94ab
Update Langauge Picker
sai-prasad-1 Aug 11, 2024
8ddd55f
Update : check for langauge folder existance
sai-prasad-1 Aug 12, 2024
942b643
Fix : DataLoader to use java data structures
sai-prasad-1 Aug 19, 2024
ecf9265
feat: Handle empty `languages` argument in LanguagePickerTag gracefully
sai-prasad-1 Aug 19, 2024
7ce1ed2
Added Config Values to
sai-prasad-1 Aug 22, 2024
2d1818f
Fix : Langauge Picker - Interative Redirect
sai-prasad-1 Aug 25, 2024
9b15ebd
feat(config): add method to convert configuration data to Java Map
sai-prasad-1 Aug 28, 2024
1fd197d
Clean up Print Statements
sai-prasad-1 Aug 28, 2024
798b9ac
Remove Unwanted Files
sai-prasad-1 Sep 2, 2024
6222ab2
Remove Empty Files
sai-prasad-1 Sep 2, 2024
bb9b8c0
move scaladoc documentation `internals` section
sai-prasad-1 Sep 2, 2024
f9c1c1f
Update scaladoc/src/dotty/tools/scaladoc/site/blocks/Tabs.scala
sai-prasad-1 Sep 2, 2024
0cab089
Enable experimental features conditionally based on flag
sai-prasad-1 Sep 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
241 changes: 241 additions & 0 deletions docs/_docs/reference/scaladoc/index.md
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be moved to a page within the "internals" section https://dotty.epfl.ch/docs/internals/index.html

Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
---
layout: doc-page
title: Scaladoc
partof: scaladoc
overview-name: Scaladoc
num: 1

---


# Custom Template Tags Documentation


## 1. Language Picker Tag

The language_picker tag is a custom tag used to render a language selection dropdown on a page. This tag allows users to switch between different language versions of the content. The available languages are determined based on the configuration and the frontmatter of the page.

### Prequisites:
- #### Configuration in config.yaml:
The available languages must be defined in the `config.yaml` file under the languages section. Each language should have a corresponding code and name.
Example:
```yaml
languages:
- code: en
name: English
- code: ja
name: Japanese
```
- #### Language Folders in `_docs`:
For each language code specified in config.yaml, there should be a corresponding folder in the _docs directory. These folders contain the translated content for each language.
Example:
```
_docs/
├── en/
├── ja/

```
- #### Frontmatter in the page:
Each page should have a `languages` key in its frontmatter that lists the available languages for that page. This ensures that the language picker dropdown is rendered with the correct options.

Example:
```yaml
---
title: Getting Started
languages: ['en', 'ja']
---
```

### Usage:

```html
<div class="language-picker-container">
{ % if page.languages % }
{ % language_picker languages=page.languages % }
{ % endif %}
</div>
```

### Considerations

- Empty Language List: If the languages list in the frontmatter is empty, the language_picker tag will not render anything.
- Default Language: If the selected language is en, the language code will be removed from the URL, treating it as the default language.
- Folder Structure: Ensure that the corresponding language folders exist in the _docs directory; otherwise, the language switch will lead to broken links.



## 1. Include Tag

### Purpose:
The `{ % include % }` tag is used to include content from other files or templates within your page. This helps in reusing common elements across multiple pages, such as headers, footers, or any other repeated content.

### Usage:
```html
{ % include 'filename' variable1='value1' variable2='value2' % }
```


### Example:
```html
{ % include 'header.html' title='Welcome to My Website' % }
```

### How it Works:
- File Inclusion: The tag includes the specified file from a predefined folder.
- Passing Variables: You can pass variables when including a file, which can be accessed within the included file using the syntax {{ include.variable1 }}.
- Accessing Variables: Inside the included file, the passed variables can be accessed like regular variables, allowing dynamic content based on the context of inclusion.



## 2. Tabs Block

### Purpose:
The { % tabs % } block is used to group content into tabs, allowing users to switch between different content sections without reloading the page. This is useful for organizing content that has multiple views, like installation instructions for different platforms.

### Usage:
```html
{ % tabs unique-tabs-id class='additional-class' % }
{ % tab 'Tab 1' for='unique-tabs-id' % }
<!-- Content for Tab 1 -->
{ % endtab % }

{ % tab 'Tab 2' for='unique-tabs-id' % }
<!-- Content for Tab 2 -->
{ % endtab % }

{ % endtabs % }
```

### Example:

```html
{ % tabs install-instructions % }
{ % tab 'Windows' for='install-instructions' % }

{ % endtab % }

{ % tab 'macOS' for='install-instructions' % }

{ % endtab % }

{ % endtabs % }
```

How it Works:
- Tabs Block: The { % tabs % } tag creates a container for multiple tabs.
- Tab Content: Each { % tab % } tag defines the content of a single tab. The for attribute must match the id specified in the { % tabs % } block to correctly link the tab content.
- Default Tab: You can specify a default tab by using the defaultTab keyword in the { % tab % } tag.
- Styling: You can apply additional CSS classes to the tabs by specifying the class attribute


## 2. AltDetails Block

### Purpose:
The { % altDetails % } tag is used to create switchable sections, often referred to as tabs. This is useful for including optional or advanced information that doesn’t need to be visible by default.

### Usage:

```html
{ % altDetails 'unique-id' 'Title of the Section' % }
<!-- Content inside the dropdown -->
{ % endaltDetails %}

```

### How it Works:

- Collapsible Section: The { % altDetails % } tag creates a section that can be expanded or collapsed by the user.
- Title and ID: The first argument is a unique identifier for the section, and the second argument is the title displayed when the section is collapsed.
- Content: Any content placed within the { % altDetails % } and { % endaltDetails % } tags is hidden by default and can be revealed by clicking on the title.


# `_data` Folder

The _data folder in your project is used to store YAML, JSON, or CSV files that contain structured data. These files can then be accessed throughout your site using the site.data variable. This is particularly useful for storing site-wide information, such as navigation menus, reusable content blocks, or any other structured data that needs to be referenced across multiple pages.

### Structure and Access
- Folder Location: The _data folder should be located in the root directory of your project.

- File Formats: The _data folder supports .yml, .yaml, .json, and .csv file formats. Each file represents a collection of data that can be accessed using the site.data variable.

- Accessing Data:
- The data within a YAML file can be accessed using `site.data.<file_name>.<key>.`
- Replace `<file_name>` with the name of the file (without the extension), and `<key>` with the specific key within the file.

### Example

Suppose you have a YAML file `_data/navigation.yml` with the following content:

```yaml
main_menu:
- name: Home
url: /
- name: About
url: /about/
- name: Contact
url: /contact/
```

You can access the data in this file using the following syntax:

```html
<ul>
{ % for item in site.data.navigation.main_menu % }
<li><a href="{ { item.url } }">{ { item.name } }</a></li>
{ % endfor % }
</ul>
```

### Considerations
- File Naming: Make sure the file names are unique to avoid conflicts when accessing data.
- Data Structure: Keep the data structure consistent across different files to avoid confusion when accessing data in your templates.

<aside class="info">
<div class='icon'></div>
<div class='content'>
It is preferred to avoid using hyphens in naming conventions. When using LIQP tags, hyphens can be misinterpreted as minus signs, leading to unexpected errors.
<br/>
{ { site.data.example['key-with-hyphen'] } }

</div>
</aside>





# Configuration File

The `_config.yaml` file is a central configuration file located in the root directory of your project. It is used to define site-wide settings and variables that can be accessed throughout your site using the `site` variable.

### Structure and Access

- File Location: The _config.yaml file should be placed in the root directory of your project.

- YAML Structure: The file uses YAML syntax to define configuration options. You can define any key-value pairs in this file, and they will be accessible via the site.config.`<key>` variable in your templates.

- Accessing Configuration:
- Any configuration setting defined in _config.yaml can be accessed using site.`<key>`.
- For example, if you define a key title: My Website, you can access it using site.title.

### Example
If your _config.yaml includes:
```yaml
site_name: My Awesome Site
description: A description of my awesome site.
```
You can access these values in your templates:
```html
{ { site.config.site_name } } <!-- Outputs: My Awesome Site -->
```



<aside class="info">
<div class='icon'></div>
<div class='content'>
It is preferred to avoid using hyphens in naming conventions. When using LIQP tags, hyphens can be misinterpreted as minus signs, leading to unexpected errors.
<br/>
{ { site.data.example['key-with-hyphen'] } }
</div>
</aside>
2 changes: 1 addition & 1 deletion project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1776,7 +1776,7 @@ object Build {
bundleCSS.taskValue
),
libraryDependencies ++= Dependencies.flexmarkDeps ++ Seq(
"nl.big-o" % "liqp" % "0.8.2",
"nl.big-o" % "liqp" % "0.9",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for context, this was necessary to support the custom syntax

"org.jsoup" % "jsoup" % "1.17.2", // Needed to process .html files for static site
Dependencies.`jackson-dataformat-yaml`,

Expand Down
3 changes: 3 additions & 0 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,8 @@ object Dependencies {
"com.vladsch.flexmark" % "flexmark-ext-yaml-front-matter" % flexmarkVersion,
)

private val snakeYamlVersion = "2.2"
val `snakeyaml` = "org.yaml" % "snakeyaml" % snakeYamlVersion

val compilerInterface = "org.scala-sbt" % "compiler-interface" % "1.9.6"
}
52 changes: 32 additions & 20 deletions project/ScaladocGeneration.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ object ScaladocGeneration {
def serialize: String =
value match {
case s: String => s"$key ${escape(s)}"
case true => s"$key"
case list: List[_] => s"$key:${list.map(x => escape(x.toString)).mkString(",")}"
case true => s"$key"
case list: List[_] =>
s"$key:${list.map(x => escape(x.toString)).mkString(",")}"
case _ =>
println(s"Unsupported setting: $key -> $value")
""
Expand Down Expand Up @@ -141,10 +142,14 @@ object ScaladocGeneration {
def key: String = "-dynamic-side-menu"
}

case class ExperimentalFeatures(value: Boolean) extends Arg[Boolean] {
def key: String = "-experimental-features"
}

import _root_.scala.reflect._

trait GenerationConfig {
def get[T <: Arg[_] : ClassTag]: Option[T]
def get[T <: Arg[_]: ClassTag]: Option[T]
def add[T <: Arg[_]](arg: T): GenerationConfig
def remove[T <: Arg[_]: ClassTag]: GenerationConfig
def withTargets(targets: Seq[String]): GenerationConfig
Expand All @@ -154,43 +159,50 @@ object ScaladocGeneration {

object GenerationConfig {
def apply(): GenerationConfig = GenerationConfigImpl(Seq.empty, Seq.empty)
def apply(targets: List[String], args: Arg[_]*): GenerationConfig = args.foldLeft(GenerationConfig()) { (config, elem) =>
config.add(elem)
}
private case class GenerationConfigImpl(targets: Seq[String], args: Seq[Arg[_]]) extends GenerationConfig {
def apply(targets: List[String], args: Arg[_]*): GenerationConfig =
args.foldLeft(GenerationConfig()) { (config, elem) =>
config.add(elem)
}
private case class GenerationConfigImpl(
targets: Seq[String],
args: Seq[Arg[_]]
) extends GenerationConfig {
override def add[T <: Arg[_]](arg: T): GenerationConfig = {
implicit val tag: ClassTag[T] = ClassTag(arg.getClass)
val (removedElem, argsWithoutElem) = argsWithout[T](tag)
removedElem.foreach(elem => println(s"$elem has been overwritten by $arg"))
removedElem.foreach(elem =>
println(s"$elem has been overwritten by $arg")
)
GenerationConfigImpl(targets, argsWithoutElem :+ arg)
}
override def remove[T <: Arg[_] : ClassTag]: GenerationConfig = {
override def remove[T <: Arg[_]: ClassTag]: GenerationConfig = {
GenerationConfigImpl(targets, argsWithout[T]._2)
}

override def get[T <: Arg[_] : ClassTag]: Option[T] = {
override def get[T <: Arg[_]: ClassTag]: Option[T] = {
val tag = implicitly[ClassTag[T]]
args.collect {
case tag(t) => t
args.collect { case tag(t) =>
t
}.headOption
}

override def withTargets(targets: Seq[String]) = copy(targets = targets)
override def serialize: String = (
args
.map(_.serialize)
++ targets
.map(_.serialize)
++ targets
).mkString(" ")

override def settings: Seq[String] =
args.map(_.serialize) ++ targets

private def argsWithout[T <: Arg[_]](
implicit tag: ClassTag[T]
): (Option[T], Seq[Arg[_]]) = args.foldLeft[(Option[T], Seq[Arg[_]])]((None, Seq.empty)) {
case ((removedElem, rest), tag(t)) => (Some(t), rest)
case ((removedElem, rest), elem) => (removedElem, rest :+ elem)
}
private def argsWithout[T <: Arg[_]](implicit
tag: ClassTag[T]
): (Option[T], Seq[Arg[_]]) =
args.foldLeft[(Option[T], Seq[Arg[_]])]((None, Seq.empty)) {
case ((removedElem, rest), tag(t)) => (Some(t), rest)
case ((removedElem, rest), elem) => (removedElem, rest :+ elem)
}
}
}

Expand Down
18 changes: 18 additions & 0 deletions scaladoc/resources/dotty_res/scripts/staticsite/alt-details.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Get all the alt-details-control elements
const altDetailsControls = document.querySelectorAll('.alt-details-control');

// Loop through each control element
altDetailsControls.forEach(control => {
// Add event listener for 'change' event
control.addEventListener('change', function() {
// Get the corresponding alt-details-detail element
const detailElement = this.nextElementSibling.nextElementSibling;

// Toggle the display of the detail element based on checkbox state
if (this.checked) {
detailElement.style.display = 'block';
} else {
detailElement.style.display = 'none';
}
});
});
Loading
Loading