Skip to content

Commit

Permalink
adding CSS fallbacks (#7)
Browse files Browse the repository at this point in the history
* adding CSS fallbacks

* more CSS cleanup

* Refactor CSS styles for twoslash popup documentation

* update logo

* Remove twoslash.svg and favicon

* cleanup unused files and deps from playground/docs

* Refactor Twoslash annotation classes

* more style cleanup

* update readme

* Refactor Twoslash annotation classes and cleanup unused files and dependencies

* add changeset
  • Loading branch information
Adammatthiesen authored Nov 10, 2024
1 parent fa42934 commit 7f43232
Show file tree
Hide file tree
Showing 20 changed files with 697 additions and 537 deletions.
5 changes: 5 additions & 0 deletions .changeset/forty-crabs-tease.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"expressive-code-twoslash": patch
---

Various CSS cleanup, and better annotation building
2 changes: 1 addition & 1 deletion docs/twoslash/astro.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default defineConfig({
tagline: "Twoslash support for Expressive Code",
favicon: "/favicon.svg",
logo: {
src: "./src/assets/twoslash.svg",
src: "./src/assets/twoslash.png",
alt: "EC Twoslash Logo",
},
credits: true,
Expand Down
1 change: 0 additions & 1 deletion docs/twoslash/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
"astro-integration-kit": "^0.17.0",
"astro-expressive-code": "^0.38.3",
"sharp": "^0.33.5",
"tailwindcss": "^3.4.14",
"expressive-code-twoslash": "workspace:*",
"starlight-package-managers": "^0.8.0",
"rehype-expressive-code": "^0.38.3"
Expand Down
213 changes: 199 additions & 14 deletions docs/twoslash/public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/twoslash/src/assets/twoslash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 0 additions & 14 deletions docs/twoslash/src/assets/twoslash.svg

This file was deleted.

2 changes: 1 addition & 1 deletion docs/twoslash/src/content/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ hero:
tagline: A plugin to add TypeScript Twoslash code examples
image:
alt: Twoslash Logo
file: ../../assets/twoslash.svg
file: ../../assets/twoslash.png
actions:
- text: Get started
link: /getting-started/installation
Expand Down
6 changes: 6 additions & 0 deletions packages/twoslash/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ Add Twoslash support to your Expressive Code TypeScript code blocks.

[Read the full documentation →](https://twoslash.matthiesen.dev)

### TODO
- [ ] Make Annotations accessible
- [ ] Implement Annotation code processesing (Requires support from EC (Planned))
- [ ] Use EC's Markdown processing system once released. (Requires support from EC (Planned))
- [ ] Figure out how to work with TwoslashVFS and setup support for "Showing Emitted Files"

## Licensing

[MIT Licensed](https://github.com/MatthiesenXYZ/EC-Plugins/tree/main/packages/twoslash/LICENSE). Made with ❤️ by [Adam Matthiesen](https://github.com/Adammatthiesen).
Expand Down
98 changes: 60 additions & 38 deletions packages/twoslash/src/annotation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
} from "twoslash";
import { customTagsIcons } from "./customTagsIcons";
import {
checkIfSingleParagraph,
defaultHoverInfoProcessor,
filterTags,
getCustomTagClass,
Expand All @@ -25,8 +26,11 @@ import {
renderMarkdownInline,
} from "./helpers";
import type { CompletionItem, CustomTagsIcon, TwoslashTag } from "./types";
import { jsdocTags } from "./regex";

export class TwoslashErrorUnderlineAnnotation extends ExpressiveCodeAnnotation {
readonly name = "twoslash-error-underline";

constructor(readonly error: NodeError) {
super({
inlineRange: {
Expand All @@ -48,6 +52,8 @@ export class TwoslashErrorUnderlineAnnotation extends ExpressiveCodeAnnotation {
* Extends the `ExpressiveCodeAnnotation` class.
*/
export class TwoslashErrorBoxAnnotation extends ExpressiveCodeAnnotation {
readonly name = "twoslash-error-box";

/**
* Creates an instance of `TwoslashErrorBoxAnnotation`.
*
Expand Down Expand Up @@ -104,6 +110,8 @@ export class TwoslashErrorBoxAnnotation extends ExpressiveCodeAnnotation {
* Extends the `ExpressiveCodeAnnotation` class to provide custom rendering for Twoslash tags.
*/
export class TwoslashCustomTagsAnnotation extends ExpressiveCodeAnnotation {
readonly name = "twoslash-custom-tags";

/**
* Creates an instance of TwoslashCustomTagsAnnotation.
* @param tag - The NodeTag object representing the Twoslash tag.
Expand Down Expand Up @@ -154,6 +162,7 @@ export class TwoslashCustomTagsAnnotation extends ExpressiveCodeAnnotation {
* Extends the ExpressiveCodeAnnotation class.
*/
export class TwoslashStaticAnnotation extends ExpressiveCodeAnnotation {
readonly name = "twoslash-static-annotation";
/**
* Creates an instance of TwoslashStaticAnnotation.
*
Expand Down Expand Up @@ -219,25 +228,30 @@ export class TwoslashStaticAnnotation extends ExpressiveCodeAnnotation {
...(this.hover.tags && this.includeJsDoc
? [
h("div.twoslash-popup-docs.twoslash-popup-docs-tags", [
h("p", [
...this.hover.tags.map((tag) =>
h("p", [
h(
"span.twoslash-popup-docs-tag-name",
`@${tag[0]}`,
),
tag[1]
? [
filterTags(tag[0]) ? " ― " : " ",
h(
"span.twoslash-popup-docs-tag-value",
renderMarkdownInline(tag[1]),
),
]
: [],
]),
),
]),
...this.hover.tags.map((tag) =>
jsdocTags.includes(tag[0])
? h("p", [
h(
"span.twoslash-popup-docs-tag-name",
`@${tag[0]}`,
),
tag[1]
? [
checkIfSingleParagraph(
tag[1],
filterTags(tag[0]),
)
? " ― "
: " ",
h(
"span.twoslash-popup-docs-tag-value",
renderMarkdownInline(tag[1]),
),
]
: [],
])
: [],
),
]),
]
: []),
Expand All @@ -254,6 +268,7 @@ export class TwoslashStaticAnnotation extends ExpressiveCodeAnnotation {
* Extends the `ExpressiveCodeAnnotation` class.
*/
export class TwoslashHighlightAnnotation extends ExpressiveCodeAnnotation {
readonly name = "twoslash-highlight-annotation";
/**
* Creates an instance of `TwoslashHighlightAnnotation`.
* @param highlight - The highlight details including start position and length.
Expand Down Expand Up @@ -284,6 +299,7 @@ export class TwoslashHighlightAnnotation extends ExpressiveCodeAnnotation {
* Extends the `ExpressiveCodeAnnotation` class to provide hover functionality.
*/
export class TwoslashHoverAnnotation extends ExpressiveCodeAnnotation {
readonly name = "twoslash-hover-annotation";
/**
* Creates an instance of `TwoslashHoverAnnotation`.
* @param hover - The hover information including character position and text.
Expand Down Expand Up @@ -339,25 +355,30 @@ export class TwoslashHoverAnnotation extends ExpressiveCodeAnnotation {
...(this.hover.tags && this.includeJsDoc
? [
h("div.twoslash-popup-docs.twoslash-popup-docs-tags", [
h("p", [
...this.hover.tags.map((tag) =>
h("p", [
h(
"span.twoslash-popup-docs-tag-name",
`@${tag[0]}`,
),
tag[1]
? [
filterTags(tag[0]) ? " ― " : " ",
h(
"span.twoslash-popup-docs-tag-value",
renderMarkdownInline(tag[1]),
),
]
: [],
]),
),
]),
...this.hover.tags.map((tag) =>
jsdocTags.includes(tag[0])
? h("p", [
h(
"span.twoslash-popup-docs-tag-name",
`@${tag[0]}`,
),
tag[1]
? [
checkIfSingleParagraph(
tag[1],
filterTags(tag[0]),
)
? " ― "
: " ",
h(
"span.twoslash-popup-docs-tag-value",
renderMarkdownInline(tag[1]),
),
]
: [],
])
: [],
),
]),
]
: []),
Expand All @@ -377,6 +398,7 @@ export class TwoslashHoverAnnotation extends ExpressiveCodeAnnotation {
* Extends the `ExpressiveCodeAnnotation` class.
*/
export class TwoslashCompletionAnnotation extends ExpressiveCodeAnnotation {
readonly name = "twoslash-completion-annotation";
/**
* Creates an instance of TwoslashCompletionAnnotation.
*
Expand Down
50 changes: 48 additions & 2 deletions packages/twoslash/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,36 @@ export function renderMarkdownInline(md: string): ElementContent[] {
return children;
}

/**
* Checks if the given markdown string consists of a single paragraph element.
*
* @param md - The markdown string to check.
* @param filterTags - A boolean indicating whether to filter tags.
* @returns A boolean indicating if the markdown string is a single paragraph element.
*/
export function checkIfSingleParagraph(
md: string,
filterTags: boolean,
): boolean {
const children = renderMarkdownInline(md);
if (filterTags) {
return !(
children.length === 1 &&
children[0].type === "element" &&
children[0].tagName === "p"
);
}
return false;
}

/**
* Filters tags based on specific keywords.
*
* @param tag - The tag string to be checked.
* @returns A boolean indicating whether the tag includes any of the specified keywords: "param", "returns", "type", or "template".
* @returns A boolean indicating whether the tag includes any of the specified keywords
*/
export function filterTags(tag: string) {
return reJsDocTagFilter.test(tag);
return !reJsDocTagFilter.test(tag);
}

/**
Expand Down Expand Up @@ -345,6 +367,7 @@ export function addErrorAnnotations(
const line = codeBlock.getLine(error.line);

if (line) {
removeHoverFromError(line, error);
line.addAnnotation(new TwoslashErrorUnderlineAnnotation(error));
line.addAnnotation(new TwoslashErrorBoxAnnotation(error, line));
}
Expand Down Expand Up @@ -501,6 +524,29 @@ export function removeHoverFromCompletions(
}
}

/**
* Removes a hover annotation from a line if it matches the specified error.
*
* @param line - The line from which to remove the hover annotation.
* @param error - The error to match against the hover annotation.
*/
export function removeHoverFromError(
line: ExpressiveCodeLine,
error: NodeError,
) {
for (const annotation of line.getAnnotations()) {
if (annotation instanceof TwoslashHoverAnnotation) {
if (
annotation.hover.start === error.start &&
annotation.hover.character === error.character &&
annotation.hover.length === error.length
) {
line.deleteAnnotation(annotation);
}
}
}
}

/**
* Processes a NodeCompletion object and returns a CompletionItem.
*
Expand Down
44 changes: 10 additions & 34 deletions packages/twoslash/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,45 +16,21 @@ import { getTwoSlashBaseStyles, twoSlashStyleSettings } from "./styles";
import type { PluginTwoslashOptions } from "./types";

/**
* A Expressive Code Plugin that transforms code blocks with Twoslash annotations.
*
* @param options - Configuration options for the plugin.
* @param options.explicitTrigger - A boolean or RegExp to explicitly trigger the transformation. Defaults to `true`.
* @param options.languages - An array of languages to apply the transformation to. Defaults to `["ts", "tsx"]`.
* @param options.twoslashOptions - Additional options to pass to the twoslasher.
*
* @example
* ```ts
* import { defineConfig } from "astro/config";
* import starlight from "@astrojs/starlight";
* import ecTwoSlash from "expressive-code-twoslash";
*
* // https://astro.build/config
* export default defineConfig({
* integrations: [
* starlight({
* title: "Starlight",
* expressiveCode: {
* plugins: [ecTwoSlash()],
* },
* }),
* ],
* });
* ```
* Add Twoslash support to your Expressive Code TypeScript code blocks.
*
* @param {PluginTwoslashOptions} options - Configuration options for the plugin.
* @param {Boolean | RegExp} options.explicitTrigger - Settings for the explicit trigger.
* @param {String[]} options.languages - The languages to apply this transformer to.
* @param {Boolean} options.includeJsDoc - If `true`, includes JSDoc comments in the hover popup.
* @param {PluginTwoslashOptions['twoslashOptions']} options.twoslashOptions - Options to forward to `twoslash`.
* @see https://twoslash.matthiesen.dev for the full documentation.
* @returns A plugin object with the specified configuration.
*/
export default function ecTwoSlash(
options: PluginTwoslashOptions = {},
): ExpressiveCodePlugin {
/**
* Destructures the options object to extract configuration settings.
*
* @param options - The options object containing configuration settings.
* @param options.explicitTrigger - Determines if the trigger should be explicit. Defaults to `true`.
* @param options.languages - An array of languages to be included. Defaults to `["ts", "tsx"]`.
* @param options.includeJsDoc - Indicates whether to include JSDoc comments. Defaults to `true`.
* @param options.twoslashOptions - Additional options for the twoslash plugin.
*/
const {
explicitTrigger = true,
Expand Down Expand Up @@ -97,9 +73,6 @@ export default function ecTwoSlash(
// Replace the EC code block with the twoslash code block
replaceECBlockWithTwoslashBlock(twoslash, codeBlock);

// Generate the error annotations
addErrorAnnotations(twoslash, codeBlock);

// Generate the Hover and Static Annotations
addHoverOrStaticAnnotations(twoslash, codeBlock, includeJsDoc);

Expand All @@ -109,6 +82,9 @@ export default function ecTwoSlash(
// Generate the Twoslash Highlight annotations
addHighlightAnnotations(twoslash, codeBlock);

// Generate the error annotations
addErrorAnnotations(twoslash, codeBlock);

// Generate the Custom Tag annotations
addCustomTagAnnotations(twoslash, codeBlock);
}
Expand Down
Loading

0 comments on commit 7f43232

Please sign in to comment.