Skip to content

Commit

Permalink
feat: ✨ plug in slack broadcast tokenizer
Browse files Browse the repository at this point in the history
  • Loading branch information
themashcodee committed Jun 27, 2024
1 parent 1cd6a8f commit 2ff58e4
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 50 deletions.
3 changes: 3 additions & 0 deletions src/utils/markdown_parser/elements/paragraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
Emphasis,
InlineCode,
Link,
SlackBroadcast,
SlackChannelMention,
SlackUserGroupMention,
SlackUserMention,
Expand Down Expand Up @@ -33,6 +34,8 @@ export const Paragraph = (props: Props) => {
return <SlackChannelMention key={i} element={subelement} />;
if (subelement.type === "slack_user_group_mention")
return <SlackUserGroupMention key={i} element={subelement} />;
if (subelement.type === "slack_broadcast")
return <SlackBroadcast key={i} element={subelement} />;

return null;
})}
Expand Down
14 changes: 10 additions & 4 deletions src/utils/markdown_parser/parser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ import {
SlackUserMentionTokenizer,
SlackChannelMentionTokenizer,
SlackUserGroupMentionTokenizer,
SlackBroadcastTokenizer,
} from "./tokenizers";
import { ReactNode } from "react";

const parser = new YozoraParser()
.unmountTokenizer("@yozora/tokenizer-list")
.useTokenizer(new SlackUserMentionTokenizer())
.useTokenizer(new SlackChannelMentionTokenizer())
.useTokenizer(new SlackUserGroupMentionTokenizer());
.useTokenizer(new SlackUserGroupMentionTokenizer())
.useTokenizer(new SlackBroadcastTokenizer());

type Options = {
markdown: boolean;
Expand All @@ -23,9 +25,7 @@ type Options = {
};

// #region HELPER CODE
// TODO: HANDLE DATE PARSING
// TODO: HANDLE @HERE, @EVERYONE, @CHANNEL PARSING (class - slack_broadcast)
// // ...(hooks.date && { date: hooks.date }),
// TODO: HANDLE DATE PARSING ...(hooks.date && { date: hooks.date }),
// #endregion

export const markdown_parser = (markdown: string, options: Options): ReactNode => {
Expand All @@ -43,6 +43,12 @@ export const markdown_parser = (markdown: string, options: Options): ReactNode =
text_string = text_string.replace(/<([^|>]+)\|([^>]+)>/g, "[$2]($1)");
// REPLACE \n\n WITH '[[DOUBLE_LINE_BREAK]]' to prevent @yozora/parser to eat it
text_string = text_string.replace(/\n\n/g, "[[DOUBLE_LINE_BREAK]]");
// REPLACE <!here> with @here
text_string = text_string.replace(/<!here>/g, "@here");
// REPLACE <!everyone> with @everyone
text_string = text_string.replace(/<!everyone>/g, "@everyone");
// REPLACE <!channel> with @channel
text_string = text_string.replace(/<!channel>/g, "@channel");

const parsed_data = parser.parse(text_string);

Expand Down
2 changes: 2 additions & 0 deletions src/utils/markdown_parser/sub_elements/emphasis.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { EmphasisSubElement } from "../types";
import { Delete } from "./delete";
import { SlackBroadcast } from "./slack_broadcast";
import { SlackChannelMention } from "./slack_channel_mention";
import { SlackUserGroupMention } from "./slack_user_group_mention";
import { SlackUserMention } from "./slack_user_mention";
Expand All @@ -22,6 +23,7 @@ export const Emphasis = (props: Props) => {
return <SlackChannelMention key={i} element={child} />;
if (child.type === "slack_user_group_mention")
return <SlackUserGroupMention key={i} element={child} />;
if (child.type === "slack_broadcast") return <SlackBroadcast key={i} element={child} />;

return <Text key={i} element={child} />;
})}
Expand Down
1 change: 1 addition & 0 deletions src/utils/markdown_parser/sub_elements/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from "./link";
export * from "./slack_user_mention";
export * from "./slack_channel_mention";
export * from "./slack_user_group_mention";
export * from "./slack_broadcast";
17 changes: 17 additions & 0 deletions src/utils/markdown_parser/sub_elements/slack_broadcast.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useGlobalData } from "../../../store";
import { SlackBroadcastSubElement } from "../types";

type Props = {
element: SlackBroadcastSubElement;
};

export const SlackBroadcast = (props: Props) => {
const { element } = props;
const { hooks } = useGlobalData();

if (element.value === "here" && hooks.atHere) return <>{hooks.atHere()}</>;
if (element.value === "everyone" && hooks.atEveryone) return <>{hooks.atEveryone()}</>;
if (element.value === "channel" && hooks.atChannel) return <>{hooks.atChannel()}</>;

return <span className="slack_broadcast">@{element.value}</span>;
};
2 changes: 2 additions & 0 deletions src/utils/markdown_parser/sub_elements/strong.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { StrongSubElement } from "../types";
import { Delete } from "./delete";
import { SlackBroadcast } from "./slack_broadcast";
import { SlackChannelMention } from "./slack_channel_mention";
import { SlackUserGroupMention } from "./slack_user_group_mention";
import { SlackUserMention } from "./slack_user_mention";
Expand All @@ -22,6 +23,7 @@ export const Strong = (props: Props) => {
return <SlackChannelMention key={i} element={child} />;
if (child.type === "slack_user_group_mention")
return <SlackUserGroupMention key={i} element={child} />;
if (child.type === "slack_broadcast") return <SlackBroadcast key={i} element={child} />;

return <Text element={child} key={i} />;
})}
Expand Down
81 changes: 37 additions & 44 deletions src/utils/markdown_parser/tokenizers/slack_broadcast/match.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import type { INodeInterval, INodePoint } from "@yozora/character";
import type { INodePoint } from "@yozora/character";
import { AsciiCodePoint } from "@yozora/character";
import type {
IMatchInlineHookCreator,
IResultOfFindDelimiters,
IResultOfProcessSingleDelimiter,
ITokenDelimiter,
} from "@yozora/core-tokenizer";
import { eatOptionalCharacters } from "@yozora/core-tokenizer";
import { SlackBroadcastType, type IDelimiter, type IThis, type IToken, type T } from "./types";

export const match: IMatchInlineHookCreator<T, IDelimiter, IToken, IThis> = function (api) {
Expand All @@ -17,68 +15,63 @@ export const match: IMatchInlineHookCreator<T, IDelimiter, IToken, IThis> = func
const blockStartIndex: number = api.getBlockStartIndex();
const blockEndIndex: number = api.getBlockEndIndex();

const potentialDelimiters: ITokenDelimiter[] = [];
const targets = ["@everyone", "@here", "@channel"];
const potentialDelimiters: IDelimiter[] = [];

for (let i = blockStartIndex; i < blockEndIndex; ++i) {
const c = nodePoints[i]?.codePoint;
if (
c === AsciiCodePoint.OPEN_ANGLE &&
nodePoints[i + 1]?.codePoint === AsciiCodePoint.NUMBER_SIGN
) {
const j = eatOptionalCharacters(nodePoints, i + 2, blockEndIndex, AsciiCodePoint.AT_SIGN);
if (j < blockEndIndex) {
potentialDelimiters.push({
type: "opener",
startIndex: i,
endIndex: j,
});
if (nodePoints[i]?.codePoint === AsciiCodePoint.AT_SIGN) {
for (const target of targets) {
if (matchTarget(nodePoints, i, target)) {
potentialDelimiters.push({
type: "full",
startIndex: i,
endIndex: i + target.length,
thickness: target.length,
});
i += target.length - 1; // Skip past the matched target
break;
}
}
} else if (c === AsciiCodePoint.CLOSE_ANGLE) {
potentialDelimiters.push({
type: "closer",
startIndex: i,
endIndex: i + 1,
});
}
}

let pIndex = 0;
let lastEndIndex = -1;
let delimiter: IDelimiter | null = null;
let currentDelimiter: IDelimiter | null = null;
while (pIndex < potentialDelimiters.length) {
const [startIndex, endIndex] = yield delimiter;
const [startIndex, endIndex] = yield currentDelimiter;

if (lastEndIndex === endIndex) {
if (delimiter == null || delimiter.startIndex >= startIndex) continue;
if (currentDelimiter == null || currentDelimiter.startIndex >= startIndex) continue;
}
lastEndIndex = endIndex;

let openerDelimiter: INodeInterval | null = null;
let closerDelimiter: INodeInterval | null = null;
for (; pIndex < potentialDelimiters.length; ++pIndex) {
const delimiter = potentialDelimiters[pIndex]!;
if (delimiter.startIndex >= startIndex && delimiter.type !== "closer") {
openerDelimiter = delimiter;
if (delimiter.startIndex >= startIndex) {
currentDelimiter = {
type: "full",
startIndex: delimiter.startIndex,
endIndex: delimiter.endIndex,
thickness: delimiter.thickness,
};
break;
}
}
}
}

for (let i = pIndex + 1; i < potentialDelimiters.length; ++i) {
const delimiter = potentialDelimiters[i]!;
if (delimiter.type === "closer") {
closerDelimiter = delimiter;
break;
}
function matchTarget(
nodePoints: ReadonlyArray<INodePoint>,
startIndex: number,
target: string,
): boolean {
for (let j = 0; j < target.length; ++j) {
if (nodePoints[startIndex + j]?.codePoint !== target.charCodeAt(j)) {
return false;
}

if (closerDelimiter == null) return;

delimiter = {
type: "full",
startIndex: openerDelimiter!.startIndex,
endIndex: closerDelimiter.endIndex,
thickness: closerDelimiter.endIndex - closerDelimiter.startIndex,
};
}
return true;
}

function processSingleDelimiter(
Expand Down
4 changes: 2 additions & 2 deletions src/utils/markdown_parser/tokenizers/slack_broadcast/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ export const parse: IParseInlineHookCreator<T, IToken, INode, IThis> = function
parse: (tokens) =>
tokens.map((token) => {
const nodePoints: ReadonlyArray<INodePoint> = api.getNodePoints();
let startIndex: number = token.startIndex + 2; // Skip `<#`
let endIndex: number = token.endIndex - 1; // Skip `>`
let startIndex: number = token.startIndex + 1; // skip @
let endIndex: number = token.endIndex;

const value = calcStringFromNodePoints(nodePoints, startIndex, endIndex);
const node: INode = api.shouldReservePosition
Expand Down
8 changes: 8 additions & 0 deletions src/utils/markdown_parser/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
export type SlackBroadcastSubElement = {
type: "slack_broadcast";
value: "here" | "everyone" | "channel";
};

export type SlackUserMentionSubElement = {
type: "slack_user_mention";
value: string;
Expand Down Expand Up @@ -31,6 +36,7 @@ export type EmphasisSubElement = {
| SlackUserMentionSubElement
| SlackChannelMentionSubElement
| SlackUserGroupMentionSubElement
| SlackBroadcastSubElement
)[];
};

Expand All @@ -42,6 +48,7 @@ export type StrongSubElement = {
| SlackUserMentionSubElement
| SlackChannelMentionSubElement
| SlackUserGroupMentionSubElement
| SlackBroadcastSubElement
)[];
};

Expand All @@ -68,6 +75,7 @@ export type ParagraphElement = {
| SlackUserMentionSubElement
| SlackChannelMentionSubElement
| SlackUserGroupMentionSubElement
| SlackBroadcastSubElement
)[];
};

Expand Down

0 comments on commit 2ff58e4

Please sign in to comment.