Skip to content

Commit

Permalink
chore: fixes for shiki
Browse files Browse the repository at this point in the history
  • Loading branch information
drewstone committed Nov 4, 2024
1 parent 3a6159d commit ef581df
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 37 deletions.
45 changes: 8 additions & 37 deletions components/GithubFileReaderDisplay.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect, useState } from "react";
import { FaGithub, FaLink, FaSpinner } from "react-icons/fa";
import { getHighlighter, type Highlighter, bundledLanguages } from "shiki";
import { useTheme } from "next-themes";
import { dedentCode, getLanguage, getShikiHighlighter } from "@/pages/shiki";

interface GithubFileReaderDisplayProps {
url: string;
Expand All @@ -10,42 +10,12 @@ interface GithubFileReaderDisplayProps {
title?: string;
}

// Create a singleton promise for the highlighter
let highlighterPromise: Promise<Highlighter> | null = null;

// Singleton function to get or create the highlighter
const getShikiHighlighter = () => {
if (!highlighterPromise) {
highlighterPromise = getHighlighter({
themes: ["github-dark", "github-light"],
langs: Object.keys(bundledLanguages),
});
}
return highlighterPromise;
};

// Language detection utility
const getLanguage = (url: string) => {
const extension = url.split(".").pop()?.toLowerCase();
switch (extension) {
case "rs":
return "rust";
case "ts":
case "tsx":
return "typescript";
case "js":
case "jsx":
return "javascript";
default:
return "plaintext";
}
};

const GithubFileReaderDisplay: React.FC<GithubFileReaderDisplayProps> = ({
url,
fromLine = 1,
toLine,
title,
dedent = true,
}) => {
const [content, setContent] = useState<string>("");
const [loading, setLoading] = useState(true);
Expand All @@ -71,7 +41,12 @@ const GithubFileReaderDisplay: React.FC<GithubFileReaderDisplayProps> = ({
const text = await response.text();
const lines = text.split("\n");
const selectedLines = lines.slice(fromLine - 1, toLine || lines.length);
const codeContent = selectedLines.join("\n");
let codeContent = selectedLines.join("\n");

// Apply dedentation if enabled
if (dedent) {
codeContent = dedentCode(codeContent);
}

// Set the theme based on current theme
const theme = currentTheme === "dark" ? "github-dark" : "github-light";
Expand All @@ -80,10 +55,6 @@ const GithubFileReaderDisplay: React.FC<GithubFileReaderDisplayProps> = ({
const highlightedCode = highlighter.codeToHtml(codeContent, {
lang: getLanguage(url),
theme: theme,
lineOptions: Array.from({ length: selectedLines.length }, (_, i) => ({
line: i + 1,
classes: [`line-${i + fromLine}`],
})),
});

// Wrap the highlighted code with a div that sets the starting line number
Expand Down
4 changes: 4 additions & 0 deletions pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { ReactElement, ReactNode } from "react";
import "../globals.css";
import { GoogleAnalytics } from "@next/third-parties/google";
import { AskCookbook } from "../components/AskCookbook";
import { getShikiHighlighter } from "./shiki";

export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
getLayout?: (page: ReactElement) => ReactNode;
Expand All @@ -24,6 +25,9 @@ if (typeof window !== "undefined" && !("requestIdleCallback" in window)) {
window.cancelIdleCallback = (id) => clearTimeout(id);
}

// Initialize the highlighter when the app starts
getShikiHighlighter();

export default function Nextra({ Component, pageProps }: NextraAppProps) {
// Define a layout if it doesn't exist in the page component
const getLayout = Component.getLayout || ((page) => page);
Expand Down
56 changes: 56 additions & 0 deletions pages/shiki.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { createHighlighter, type Highlighter, bundledLanguages } from "shiki";

// Create a singleton promise for the highlighter
let highlighterPromise: Promise<Highlighter> | null = null;

// Singleton function to get or create the highlighter
export const getShikiHighlighter = () => {
if (!highlighterPromise) {
highlighterPromise = createHighlighter({
themes: ["github-dark", "github-light"],
langs: Object.keys(bundledLanguages),
});
}
return highlighterPromise;
};

// Language detection utility
export const getLanguage = (url: string) => {
const extension = url.split(".").pop()?.toLowerCase();
switch (extension) {
case "rs":
return "rust";
case "ts":
case "tsx":
return "typescript";
case "js":
case "jsx":
return "javascript";
default:
return "plaintext";
}
};

export const dedentCode = (code: string): string => {
const lines = code.split("\n");

// Filter out empty lines for calculating minimum indentation
const nonEmptyLines = lines.filter((line) => line.trim().length > 0);

if (nonEmptyLines.length === 0) return code;

// Find the minimum indentation level across all non-empty lines
const minIndent = Math.min(
...nonEmptyLines.map((line) => {
const match = line.match(/^[ \t]*/);
return match ? match[0].length : 0;
}),
);

// Remove the common indentation from all lines
if (minIndent > 0) {
return lines.map((line) => line.slice(minIndent)).join("\n");
}

return code;
};

0 comments on commit ef581df

Please sign in to comment.