Skip to content

Commit dbcca4a

Browse files
authored
feat: generate Javadoc link text automatically (#688)
* feat: generate Javadoc link text automatically * Apply suggestion from @zlataovce
1 parent 68d5526 commit dbcca4a

File tree

3 files changed

+55
-22
lines changed

3 files changed

+55
-22
lines changed

CONTRIBUTING.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,10 @@ For that, you can use the `jd:project_name[:module_name][:class_or_member_refere
180180
[`repeat(long, TimeUnit)`](jd:velocity:com.velocitypowered.api.scheduler.Scheduler$TaskBuilder#repeat(long,java.util.concurrent.TimeUnit))
181181
[java.base's List](jd:java:java.util.List)
182182
[java.sql's Connection](jd:java:java.sql:java.sql.Connection)
183+
184+
We don't even need to specify the link text, it can be generated automatically:
185+
[](jd:paper:org.bukkit.event.Event) looks like `Event`
186+
[](jd:velocity:com.velocitypowered.api.scheduler.Scheduler$TaskBuilder#repeat(long,java.util.concurrent.TimeUnit)) looks like `Scheduler.TaskBuilder#repeat(long, TimeUnit)`
183187
```
184188

185189
## Referencing a build system dependency

src/content/docs/paper/dev/api/event-api/chat-event.md

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,21 @@ slug: paper/dev/chat-events
55
---
66

77
The chat event has evolved a few times over the years.
8-
This guide will explain how to properly use the new [`AsyncChatEvent`](jd:paper:io.papermc.paper.event.player.AsyncChatEvent)
9-
and its [`ChatRenderer`](jd:paper:io.papermc.paper.chat.ChatRenderer).
10-
The [`AsyncChatEvent`](jd:paper:io.papermc.paper.event.player.AsyncChatEvent)
11-
is an improved version of the old [`AsyncPlayerChatEvent`](jd:paper:org.bukkit.event.player.AsyncPlayerChatEvent)
8+
This guide will explain how to properly use the new [](jd:paper:io.papermc.paper.event.player.AsyncChatEvent)
9+
and its [](jd:paper:io.papermc.paper.chat.ChatRenderer).
10+
The [](jd:paper:io.papermc.paper.event.player.AsyncChatEvent)
11+
is an improved version of the old [](jd:paper:org.bukkit.event.player.AsyncPlayerChatEvent)
1212
that allows you to render chat messages individually for each player.
1313

1414
:::note[`AsyncChatEvent` vs `ChatEvent`]
1515

16-
The key difference between [`AsyncChatEvent`](jd:paper:io.papermc.paper.event.player.AsyncChatEvent)
17-
and [`ChatEvent`](jd:paper:io.papermc.paper.event.player.ChatEvent) is that
18-
[`AsyncChatEvent`](jd:paper:io.papermc.paper.event.player.AsyncChatEvent) is fired asynchronously.
16+
The key difference between [](jd:paper:io.papermc.paper.event.player.AsyncChatEvent)
17+
and [](jd:paper:io.papermc.paper.event.player.ChatEvent) is that
18+
[](jd:paper:io.papermc.paper.event.player.AsyncChatEvent) is fired asynchronously.
1919

2020
This means that it does not block the main thread and sends the chat message when the listener has completed.
2121
Be aware that using the Bukkit API in an asynchronous context (i.e. the event handler) is unsafe and exceptions may be thrown.
22-
If you need to use the Bukkit API, you can use [`ChatEvent`](jd:paper:io.papermc.paper.event.player.ChatEvent).
22+
If you need to use the Bukkit API, you can use [](jd:paper:io.papermc.paper.event.player.ChatEvent).
2323
However, we recommend using [`BukkitScheduler`](/paper/dev/scheduler).
2424

2525
:::
@@ -28,10 +28,10 @@ However, we recommend using [`BukkitScheduler`](/paper/dev/scheduler).
2828

2929
Before we can start using the new chat event, we need to understand how the new renderer works.
3030
The renderer is Paper's way of allowing plugins to modify the chat message before it is sent to the player.
31-
This is done by using the [`ChatRenderer`](jd:paper:io.papermc.paper.chat.ChatRenderer) interface with its
32-
[`render`](jd:paper:io.papermc.paper.chat.ChatRenderer#render(org.bukkit.entity.Player,net.kyori.adventure.text.Component,net.kyori.adventure.text.Component,net.kyori.adventure.audience.Audience))
33-
method. Previously, this was done by using the [`AsyncPlayerChatEvent`](jd:paper:org.bukkit.event.player.AsyncPlayerChatEvent)
34-
with its [`setFormat`](jd:paper:org.bukkit.event.player.AsyncPlayerChatEvent#setFormat(java.lang.String)) method.
31+
This is done by using the [](jd:paper:io.papermc.paper.chat.ChatRenderer) interface with its
32+
[](jd:paper:io.papermc.paper.chat.ChatRenderer#render(org.bukkit.entity.Player,net.kyori.adventure.text.Component,net.kyori.adventure.text.Component,net.kyori.adventure.audience.Audience))
33+
method. Previously, this was done by using the [](jd:paper:org.bukkit.event.player.AsyncPlayerChatEvent)
34+
with its [](jd:paper:org.bukkit.event.player.AsyncPlayerChatEvent#setFormat(java.lang.String)) method.
3535

3636
```java title="ChatRenderer#render"
3737
public Component render(Player source, Component sourceDisplayName, Component message, Audience viewer) {
@@ -48,27 +48,27 @@ public Component render(Player source, Component sourceDisplayName, Component me
4848
:::tip[`ChatRenderer.ViewerUnaware`]
4949

5050
If your renderer does not need to know about the viewer, you can use the
51-
[`ChatRenderer.ViewerUnaware`](jd:paper:io.papermc.paper.chat.ChatRenderer$ViewerUnaware)
52-
interface instead of the [`ChatRenderer`](jd:paper:io.papermc.paper.chat.ChatRenderer) interface.
51+
[](jd:paper:io.papermc.paper.chat.ChatRenderer$ViewerUnaware)
52+
interface instead of the [](jd:paper:io.papermc.paper.chat.ChatRenderer) interface.
5353
This will benefit performance as the message will only be rendered once instead of each individual player.
5454

5555
:::
5656

5757
## Using the renderer
5858

5959
There are two ways to use the renderer.
60-
1. Implementing the [`ChatRenderer`](jd:paper:io.papermc.paper.chat.ChatRenderer) interface in a class.
60+
1. Implementing the [](jd:paper:io.papermc.paper.chat.ChatRenderer) interface in a class.
6161
2. Using a lambda expression.
6262

6363
Depending on the complexity of your renderer, you may want to use one or the other.
6464

6565
### Implementing the `ChatRenderer` interface
6666

67-
The first way of using the renderer is by implementing the [`ChatRenderer`](jd:paper:io.papermc.paper.chat.ChatRenderer)
67+
The first way of using the renderer is by implementing the [](jd:paper:io.papermc.paper.chat.ChatRenderer)
6868
interface in a class. In this example, we will be using our `ChatListener` class.
6969

7070
Next, we need to tell the event to use the renderer by using the
71-
[`renderer`](jd:paper:io.papermc.paper.event.player.AbstractChatEvent#renderer()) method.
71+
[](jd:paper:io.papermc.paper.event.player.AbstractChatEvent#renderer()) method.
7272

7373
```java title="ChatListener.java"
7474
public class ChatListener implements Listener, ChatRenderer { // Implement the ChatRenderer and Listener interface

src/utils/remark/javadoc.ts

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,27 @@ const asUrl = (name: string): string => {
2323
return `${name0}.html` + (hash ? `#${hash}` : "");
2424
};
2525

26+
const asRef = (name: string): string => {
27+
let [name0, hash] = name.split("#", 2);
28+
name0 = name0.replaceAll(".", "/");
29+
30+
const lastSlash = name0.lastIndexOf("/");
31+
if (lastSlash !== -1) {
32+
// remove package
33+
name0 = name0.substring(lastSlash + 1);
34+
}
35+
name0 = name0.replaceAll("$", ".");
36+
37+
const parenIndex = hash?.indexOf("(");
38+
if (hash && parenIndex !== -1) {
39+
// method parameters
40+
const params = hash.substring(parenIndex + 1, hash.length - 1).split(",");
41+
hash = `${hash.substring(0, parenIndex)}(${params.map((p) => p.substring(p.lastIndexOf(".") + 1)).join(", ")})`;
42+
}
43+
44+
return name0 + (hash ? `#${hash}` : "");
45+
};
46+
2647
const error = (err: any): never => {
2748
if (process.env.NODE_ENV === "production") {
2849
console.error(err);
@@ -35,14 +56,19 @@ const error = (err: any): never => {
3556
}
3657
};
3758

38-
const parse = async (url: string, { targets }: Options): Promise<string | null> => {
59+
interface ParseResult {
60+
url?: string;
61+
ref?: string;
62+
}
63+
64+
const parse = async (url: string, { targets }: Options): Promise<ParseResult> => {
3965
const match = /^jd:(.+?)(?::(.+?))?(?::(.+?))?$/.exec(url);
4066
if (!match) {
4167
if (url.startsWith("jd:")) {
4268
error(new Error(`Failed to parse Javadoc link "${url}"`));
4369
}
4470

45-
return null; // not a Javadoc link
71+
return {}; // not a Javadoc link
4672
}
4773

4874
const target = targets[match[1]];
@@ -54,7 +80,7 @@ const parse = async (url: string, { targets }: Options): Promise<string | null>
5480

5581
const name = match[3] ?? match[2];
5682
if (!name) {
57-
return targetUrl;
83+
return { url: targetUrl };
5884
}
5985

6086
const module = match[3] ? match[2] : typeof target !== "string" ? target.module : undefined;
@@ -72,16 +98,19 @@ const parse = async (url: string, { targets }: Options): Promise<string | null>
7298
}
7399
}
74100

75-
return parsed;
101+
return { url: parsed, ref: asRef(name) };
76102
};
77103

78104
const plugin: RemarkPlugin = (options: Options) => {
79105
return async (tree) => {
80106
const promises: Promise<void>[] = [];
81107
visit(tree, "link", (node) => {
82108
promises.push(
83-
parse(node.url, options).then((url) => {
109+
parse(node.url, options).then(({ url, ref }) => {
84110
node.url = url ?? node.url;
111+
if (ref && node.children.length === 0) {
112+
node.children.push({ type: "inlineCode", value: ref });
113+
}
85114
})
86115
);
87116
});

0 commit comments

Comments
 (0)