Skip to content

Commit

Permalink
Improve inline code and code block support in RTL languages (withastr…
Browse files Browse the repository at this point in the history
…o#525)

Co-authored-by: Kevin Zuniga Cuellar <46791833+kevinzunigacuellar@users.noreply.github.com>
  • Loading branch information
delucis and kevinzunigacuellar authored Aug 21, 2023
1 parent e287d6f commit 87caf21
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/long-parrots-give.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/starlight': patch
---

Improve inline code and code block support in RTL languages
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { rehype } from 'rehype';
import { expect, test } from 'vitest';
import { rehypeRtlCodeSupport } from '../../integrations/code-rtl-support';

const processor = rehype().data('settings', { fragment: true }).use(rehypeRtlCodeSupport());

test('applies `dir="auto"` to inline code', async () => {
const input = `<p>Some text with <code>inline code</code>.</p>`;
const output = String(await processor.process(input));
expect(output).not.toEqual(input);
expect(output).includes('dir="auto"');
expect(output).toMatchInlineSnapshot(
'"<p>Some text with <code dir=\\"auto\\">inline code</code>.</p>"'
);
});

test('applies `dir="ltr"` to code blocks', async () => {
const input = `<p>Some text in a paragraph:</p><pre><code>console.log('test')</code></pre>`;
const output = String(await processor.process(input));
expect(output).not.toEqual(input);
expect(output).includes('dir="ltr"');
expect(output).toMatchInlineSnapshot(
'"<p>Some text in a paragraph:</p><pre dir=\\"ltr\\"><code>console.log(\'test\')</code></pre>"'
);
});
3 changes: 3 additions & 0 deletions packages/starlight/__tests__/remark-rehype/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { defineVitestConfig } from '../test-config';

export default defineVitestConfig({ title: 'Remark-Rehype' });
2 changes: 2 additions & 0 deletions packages/starlight/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { starlightSitemap } from './integrations/sitemap';
import { vitePluginStarlightUserConfig } from './integrations/virtual-user-config';
import { errorMap } from './utils/error-map';
import { StarlightConfigSchema, StarlightUserConfig } from './utils/user-config';
import { rehypeRtlCodeSupport } from './integrations/code-rtl-support';

export default function StarlightIntegration(opts: StarlightUserConfig): AstroIntegration[] {
const parsedConfig = StarlightConfigSchema.safeParse(opts, { errorMap });
Expand Down Expand Up @@ -39,6 +40,7 @@ export default function StarlightIntegration(opts: StarlightUserConfig): AstroIn
},
markdown: {
remarkPlugins: [...starlightAsides()],
rehypePlugins: [rehypeRtlCodeSupport()],
shikiConfig:
// Configure Shiki theme if the user is using the default github-dark theme.
config.markdown.shikiConfig.theme !== 'github-dark' ? {} : { theme: 'css-variables' },
Expand Down
31 changes: 31 additions & 0 deletions packages/starlight/integrations/code-rtl-support.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { Root } from 'hastscript/lib/core';
import { CONTINUE, SKIP, visit } from 'unist-util-visit';

/**
* rehype plugin that adds `dir` attributes to `<code>` and `<pre>`
* elements that don’t already have them.
*
* `<code>` will become `<code dir="auto">`
* `<pre>` will become `<pre dir="ltr">`
*
* `<code>` _inside_ `<pre>` is skipped, so respects the `ltr` on its parent.
*
* Reasoning:
* - `<pre>` is usually a code block and code should be LTR even in an RTL document
* - `<code>` is often LTR, but could also be RTL. `dir="auto"` ensures the bidirectional
* algorithm treats the contents of `<code>` in isolation and gives its best guess.
*/
export function rehypeRtlCodeSupport() {
return () => (root: Root) => {
visit(root, 'element', (el) => {
if (el.tagName === 'pre' || el.tagName === 'code') {
el.properties ||= {};
if (!('dir' in el.properties)) {
el.properties.dir = { pre: 'ltr', code: 'auto' }[el.tagName];
}
return SKIP;
}
return CONTINUE;
});
};
}

0 comments on commit 87caf21

Please sign in to comment.