feat: simple-mode code-block highlighting, prompt, and copy rules
Add a custom Shiki grammar for the simple-mode command language (src/grammars/rdbms.mjs), registered with Expressive Code. Two language ids share it: rdbms (real commands) and rdbms-syntax (abstract templates). Simple-mode blocks now highlight; advanced examples keep sql. Separation + copy ergonomics via CSS (global.css): a decorative, copy-safe "> " prompt on rdbms command lines (not in the copy buffer), and the copy button hidden on multi-command rdbms blocks and on rdbms-syntax templates (the app input is single-line, so a multi-command paste is not runnable); single-command, sql, and sh blocks keep copy. Content: convert 22 simple-mode fences to rdbms; lead the simplest examples (first project, Tables reference) with bare "with pk" (the beginner default that creates a ready-made id key), pointing to the named form. Record the fence + prompt conventions in STYLE.md.
This commit is contained in:
@@ -80,8 +80,25 @@ This is planned and not yet available.
|
|||||||
- Where both modes apply, show the **simple-mode** form and its
|
- Where both modes apply, show the **simple-mode** form and its
|
||||||
**advanced-mode (SQL)** equivalent — the in-app teaching echo already
|
**advanced-mode (SQL)** equivalent — the in-app teaching echo already
|
||||||
pairs these, so docs mirror it.
|
pairs these, so docs mirror it.
|
||||||
|
- Prefer **worked examples** (a real command on the library schema) over
|
||||||
|
abstract prose, and **always cross-link** the related reference/guide
|
||||||
|
pages (use stubs so links resolve before a page is written).
|
||||||
- Code blocks for exact input/output; reserve casts for motion/flow.
|
- Code blocks for exact input/output; reserve casts for motion/flow.
|
||||||
|
|
||||||
|
### Code-block fences
|
||||||
|
|
||||||
|
- **Simple-mode commands → ` ```rdbms `** — custom highlight grammar in
|
||||||
|
`src/grammars/rdbms.mjs`, registered with Expressive Code in
|
||||||
|
`astro.config.mjs` (keywords + types coloured).
|
||||||
|
- **Advanced-mode SQL → ` ```sql `**; shell / install → ` ```sh `.
|
||||||
|
- A decorative `> ` prompt is prepended to `rdbms` lines via CSS
|
||||||
|
(`src/styles/global.css`) — **do not type `>` in the fence**. It is
|
||||||
|
copy-safe (Expressive Code's copy button uses `data-code`) and
|
||||||
|
`user-select:none`.
|
||||||
|
- **One command per line** in an `rdbms` block; a multi-line single
|
||||||
|
statement (e.g. advanced `CREATE TABLE`) belongs in a ` ```sql ` block,
|
||||||
|
where no prompt is added.
|
||||||
|
|
||||||
### Canonical library schema (source of truth for examples)
|
### Canonical library schema (source of truth for examples)
|
||||||
|
|
||||||
Use these exact names/types in every example:
|
Use these exact names/types in every example:
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ import starlight from '@astrojs/starlight';
|
|||||||
|
|
||||||
import tailwindcss from '@tailwindcss/vite';
|
import tailwindcss from '@tailwindcss/vite';
|
||||||
|
|
||||||
|
// Custom highlight grammar for the playground's simple-mode command language
|
||||||
|
// (advanced-mode examples use the built-in `sql` grammar). Fence with ```rdbms.
|
||||||
|
import rdbmsLang, { rdbmsSyntax } from './src/grammars/rdbms.mjs';
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
// TODO(Phase B/SEO): set `site` to the production URL once the domain is
|
// TODO(Phase B/SEO): set `site` to the production URL once the domain is
|
||||||
@@ -21,6 +25,8 @@ export default defineConfig({
|
|||||||
// public home. Omitted for now rather than linking the wrong repo.
|
// public home. Omitted for now rather than linking the wrong repo.
|
||||||
// social: [{ icon: 'github', label: 'GitHub', href: '…' }],
|
// social: [{ icon: 'github', label: 'GitHub', href: '…' }],
|
||||||
customCss: ['./src/styles/global.css'],
|
customCss: ['./src/styles/global.css'],
|
||||||
|
// Register the simple-mode command grammar with Expressive Code (Shiki).
|
||||||
|
expressiveCode: { shiki: { langs: [rdbmsLang, rdbmsSyntax] } },
|
||||||
// Pragmatic structure (ADR-0044 §7 / website/STYLE.md): Getting
|
// Pragmatic structure (ADR-0044 §7 / website/STYLE.md): Getting
|
||||||
// started, Guides, Reference, Concepts. Autogenerated per directory;
|
// started, Guides, Reference, Concepts. Autogenerated per directory;
|
||||||
// in-section order is controlled by each page's `sidebar.order`
|
// in-section order is controlled by each page's `sidebar.order`
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ the CSV files. Because it is derived, it never needs to be shared or backed
|
|||||||
up separately — and if it is ever missing or stale, the `rebuild` command
|
up separately — and if it is ever missing or stale, the `rebuild` command
|
||||||
regenerates it from the readable files:
|
regenerates it from the readable files:
|
||||||
|
|
||||||
```text
|
```rdbms
|
||||||
rebuild
|
rebuild
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ When you start the playground without naming a project, it creates a
|
|||||||
**temporary** project with an automatic name. Temporary projects are perfect
|
**temporary** project with an automatic name. Temporary projects are perfect
|
||||||
for quick experiments. When you want to keep one, give it a name:
|
for quick experiments. When you want to keep one, give it a name:
|
||||||
|
|
||||||
```text
|
```rdbms
|
||||||
save
|
save
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ keeping a course's projects together, or for testing.
|
|||||||
|
|
||||||
The `export` command packages a project as a zip you can send to anyone:
|
The `export` command packages a project as a zip you can send to anyone:
|
||||||
|
|
||||||
```text
|
```rdbms
|
||||||
export
|
export
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -15,41 +15,41 @@ and press <kbd>Enter</kbd> to run them.
|
|||||||
|
|
||||||
## Create a table
|
## Create a table
|
||||||
|
|
||||||
A table needs at least a primary key. The `with pk` clause names the
|
The quickest way to make a table is `with pk` on its own, which gives you a
|
||||||
primary-key column and its type:
|
ready-made primary key column called `id`:
|
||||||
|
|
||||||
```text
|
```rdbms
|
||||||
create table authors with pk author_id(serial)
|
create table authors with pk
|
||||||
```
|
```
|
||||||
|
|
||||||
`serial` is an auto-incrementing number — you will not have to fill it in
|
You never fill `id` in yourself — the database assigns it as you add rows.
|
||||||
yourself.
|
(You can also name and type the key yourself; see the
|
||||||
|
[Tables reference](/reference/tables/).)
|
||||||
|
|
||||||
## Add a couple of columns
|
## Add a couple of columns
|
||||||
|
|
||||||
In simple mode you create a table with its key, then add the other columns
|
In simple mode you create a table with its key, then add the other columns
|
||||||
one at a time:
|
one at a time:
|
||||||
|
|
||||||
```text
|
```rdbms
|
||||||
add column to authors: name (text)
|
add column to authors: name (text)
|
||||||
add column to authors: birth_year (int)
|
add column to authors: birth_year (int)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Add a row
|
## Add a row
|
||||||
|
|
||||||
`insert` adds a row. List the columns you are supplying — the `author_id`
|
`insert` adds a row. List the columns you are supplying — `id` fills itself
|
||||||
fills itself in because it is a `serial`:
|
in automatically:
|
||||||
|
|
||||||
```text
|
```rdbms
|
||||||
insert into authors (name, birth_year) values ('Ada Lovelace', 1815)
|
insert into authors (name, birth_year) values ('Ada Lovelace', 1815)
|
||||||
```
|
```
|
||||||
|
|
||||||
The playground shows the row it just inserted, including the generated
|
The playground shows the row it just inserted, including the generated `id`.
|
||||||
`author_id`.
|
|
||||||
|
|
||||||
## Look at the data
|
## Look at the data
|
||||||
|
|
||||||
```text
|
```rdbms
|
||||||
show data authors
|
show data authors
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ in **simple mode**, and reach for **advanced mode** when you want full SQL.
|
|||||||
Simple mode is a friendly, keyword-based command language designed for
|
Simple mode is a friendly, keyword-based command language designed for
|
||||||
learning. Commands read close to English:
|
learning. Commands read close to English:
|
||||||
|
|
||||||
```text
|
```rdbms
|
||||||
create table authors with pk author_id(serial)
|
create table authors with pk author_id(serial)
|
||||||
show data authors
|
show data authors
|
||||||
```
|
```
|
||||||
@@ -33,7 +33,7 @@ select title, published from books where published >= 2000 order by published;
|
|||||||
|
|
||||||
Switch modes with the `mode` command:
|
Switch modes with the `mode` command:
|
||||||
|
|
||||||
```text
|
```rdbms
|
||||||
mode advanced
|
mode advanced
|
||||||
mode simple
|
mode simple
|
||||||
```
|
```
|
||||||
@@ -46,7 +46,7 @@ open it, so a project set up for SQL practice reopens in advanced mode.
|
|||||||
When you are in simple mode and want to run a single SQL statement without
|
When you are in simple mode and want to run a single SQL statement without
|
||||||
switching, prefix the line with a colon:
|
switching, prefix the line with a colon:
|
||||||
|
|
||||||
```text
|
```rdbms
|
||||||
:select count(*) from books
|
:select count(*) from books
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -15,9 +15,13 @@ from scratch in simple mode: two tables, a relationship between them, and a
|
|||||||
few rows. By the end you will have used the create → add column → relate →
|
few rows. By the end you will have used the create → add column → relate →
|
||||||
insert → query loop end to end.
|
insert → query loop end to end.
|
||||||
|
|
||||||
|
We give each table a **named** primary key (like `author_id`) so the
|
||||||
|
relationship reads clearly — but `with pk` on its own gives you a default
|
||||||
|
`id` if you prefer, as in [Your first project](/getting-started/first-project/).
|
||||||
|
|
||||||
## 1. Create the authors table
|
## 1. Create the authors table
|
||||||
|
|
||||||
```text
|
```rdbms
|
||||||
create table authors with pk author_id(serial)
|
create table authors with pk author_id(serial)
|
||||||
add column to authors: name (text)
|
add column to authors: name (text)
|
||||||
add column to authors: birth_year (int)
|
add column to authors: birth_year (int)
|
||||||
@@ -25,7 +29,7 @@ add column to authors: birth_year (int)
|
|||||||
|
|
||||||
## 2. Create the books table
|
## 2. Create the books table
|
||||||
|
|
||||||
```text
|
```rdbms
|
||||||
create table books with pk book_id(serial)
|
create table books with pk book_id(serial)
|
||||||
add column to books: title (text)
|
add column to books: title (text)
|
||||||
add column to books: author_id (int)
|
add column to books: author_id (int)
|
||||||
@@ -37,7 +41,7 @@ add column to books: published (int)
|
|||||||
An author has many books, so `books.author_id` should point at
|
An author has many books, so `books.author_id` should point at
|
||||||
`authors.author_id`. Declare that one-to-many relationship:
|
`authors.author_id`. Declare that one-to-many relationship:
|
||||||
|
|
||||||
```text
|
```rdbms
|
||||||
add 1:n relationship from authors.author_id to books.author_id
|
add 1:n relationship from authors.author_id to books.author_id
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -48,7 +52,7 @@ the `books` side.
|
|||||||
|
|
||||||
`author_id` and `book_id` are `serial`, so they fill themselves in:
|
`author_id` and `book_id` are `serial`, so they fill themselves in:
|
||||||
|
|
||||||
```text
|
```rdbms
|
||||||
insert into authors (name, birth_year) values ('Ada Lovelace', 1815)
|
insert into authors (name, birth_year) values ('Ada Lovelace', 1815)
|
||||||
insert into authors (name, birth_year) values ('Alan Turing', 1912)
|
insert into authors (name, birth_year) values ('Alan Turing', 1912)
|
||||||
insert into books (title, author_id, published) values ('Notes on the Analytical Engine', 1, 1843)
|
insert into books (title, author_id, published) values ('Notes on the Analytical Engine', 1, 1843)
|
||||||
@@ -56,7 +60,7 @@ insert into books (title, author_id, published) values ('Notes on the Analytical
|
|||||||
|
|
||||||
## 5. Look at your data
|
## 5. Look at your data
|
||||||
|
|
||||||
```text
|
```rdbms
|
||||||
show data authors
|
show data authors
|
||||||
show data books
|
show data books
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -11,16 +11,24 @@ in the Reference.
|
|||||||
|
|
||||||
## Create a table (simple mode)
|
## Create a table (simple mode)
|
||||||
|
|
||||||
Every table is created with its primary key. The `with pk` clause lists the
|
The simplest way to create a table is `with pk` on its own — it gives the
|
||||||
primary-key column(s):
|
table a ready-made primary-key column named `id` that the database fills in
|
||||||
|
for you:
|
||||||
|
|
||||||
```text
|
```rdbms
|
||||||
|
create table authors with pk
|
||||||
|
```
|
||||||
|
|
||||||
|
Prefer to name the key, or give it a specific type? Name it in the `with pk`
|
||||||
|
clause:
|
||||||
|
|
||||||
|
```rdbms
|
||||||
create table authors with pk author_id(serial)
|
create table authors with pk author_id(serial)
|
||||||
```
|
```
|
||||||
|
|
||||||
Other columns are added afterwards with `add column`:
|
Either way, the rest of the columns are added afterwards with `add column`:
|
||||||
|
|
||||||
```text
|
```rdbms
|
||||||
add column to authors: name (text)
|
add column to authors: name (text)
|
||||||
add column to authors: birth_year (int)
|
add column to authors: birth_year (int)
|
||||||
```
|
```
|
||||||
@@ -30,14 +38,14 @@ add column to authors: birth_year (int)
|
|||||||
To make the primary key span more than one column, list them, comma
|
To make the primary key span more than one column, list them, comma
|
||||||
separated:
|
separated:
|
||||||
|
|
||||||
```text
|
```rdbms
|
||||||
create table loans with pk book_id(int), member_id(int)
|
create table loans with pk book_id(int), member_id(int)
|
||||||
```
|
```
|
||||||
|
|
||||||
**Syntax**
|
**Syntax**
|
||||||
|
|
||||||
```text
|
```rdbms-syntax
|
||||||
create table <Name> with pk <col>(<type>)[, ...]
|
create table <Name> with pk [<col>(<type>)[, ...]]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Create a table (advanced mode)
|
## Create a table (advanced mode)
|
||||||
@@ -58,13 +66,13 @@ keys — see the Constraints and Relationships references.
|
|||||||
|
|
||||||
**Syntax**
|
**Syntax**
|
||||||
|
|
||||||
```text
|
```rdbms-syntax
|
||||||
create table [if not exists] <Name> (<col> <type> [constraints], ...)
|
create table [if not exists] <Name> (<col> <type> [constraints], ...)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Drop a table
|
## Drop a table
|
||||||
|
|
||||||
```text
|
```rdbms
|
||||||
drop table authors
|
drop table authors
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ by hand.
|
|||||||
avoiding easily-confused characters). Always kept unique. Useful when you
|
avoiding easily-confused characters). Always kept unique. Useful when you
|
||||||
want an identifier that is compact but not guessable or sequential.
|
want an identifier that is compact but not guessable or sequential.
|
||||||
|
|
||||||
```text
|
```rdbms
|
||||||
create table members with pk member_id(serial)
|
create table members with pk member_id(serial)
|
||||||
add column to members: member_code (shortid)
|
add column to members: member_code (shortid)
|
||||||
insert into members (name) values ('Grace Hopper')
|
insert into members (name) values ('Grace Hopper')
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
// Minimal Shiki / TextMate grammar for the RDBMS Playground *simple-mode*
|
||||||
|
// command language, so docs code blocks fenced as ```rdbms get syntax
|
||||||
|
// highlighting. Advanced-mode examples use the built-in `sql` grammar.
|
||||||
|
//
|
||||||
|
// Keyword/clause vocabulary is taken from the in-app help/usage strings
|
||||||
|
// (`src/friendly/strings/en-US.yaml`) and the command grammar. Matching is
|
||||||
|
// case-insensitive (keywords are case-insensitive in the app; identifiers
|
||||||
|
// are case-preserving and intentionally left unscoped so they read as plain
|
||||||
|
// text against the coloured keywords).
|
||||||
|
//
|
||||||
|
// Two language ids share one grammar:
|
||||||
|
// - `rdbms` — real commands; gets a decorative `> ` prompt via CSS.
|
||||||
|
// - `rdbms-syntax` — abstract syntax templates (with <placeholders>); same
|
||||||
|
// highlighting, but a distinct `data-language` so the
|
||||||
|
// prompt (scoped to data-language='rdbms') is NOT added.
|
||||||
|
|
||||||
|
const patterns = [
|
||||||
|
{ include: '#comment' },
|
||||||
|
{ include: '#string' },
|
||||||
|
{ include: '#flag' },
|
||||||
|
{ include: '#number' },
|
||||||
|
{ include: '#constant' },
|
||||||
|
{ include: '#type' },
|
||||||
|
{ include: '#keyword' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const repository = {
|
||||||
|
comment: {
|
||||||
|
match: '#.*$',
|
||||||
|
name: 'comment.line.number-sign.rdbms',
|
||||||
|
},
|
||||||
|
string: {
|
||||||
|
name: 'string.quoted.single.rdbms',
|
||||||
|
begin: "'",
|
||||||
|
end: "'",
|
||||||
|
patterns: [{ match: "''", name: 'constant.character.escape.rdbms' }],
|
||||||
|
},
|
||||||
|
flag: {
|
||||||
|
match: '--[A-Za-z][A-Za-z-]*',
|
||||||
|
name: 'keyword.operator.flag.rdbms',
|
||||||
|
},
|
||||||
|
number: {
|
||||||
|
match: '\\b[0-9]+(?:\\.[0-9]+)?\\b',
|
||||||
|
name: 'constant.numeric.rdbms',
|
||||||
|
},
|
||||||
|
constant: {
|
||||||
|
match: '(?i)\\b(true|false|null)\\b',
|
||||||
|
name: 'constant.language.rdbms',
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
match: '(?i)\\b(text|int|real|decimal|bool|date|datetime|blob|serial|shortid)\\b',
|
||||||
|
name: 'support.type.rdbms',
|
||||||
|
},
|
||||||
|
keyword: {
|
||||||
|
match:
|
||||||
|
'(?i)\\b(create|table|tables|drop|add|column|with|pk|to|from|into|values|insert|update|set|where|delete|show|data|rename|change|alter|relationship|relationships|index|indexes|on|as|references|constraint|not|unique|default|check|primary|key|cascade|restrict|and|or|in|between|like|is|explain|replay|undo|redo|save|new|load|rebuild|export|import|copy|mode|help|hint|quit|messages|all|last|types|simple|advanced)\\b',
|
||||||
|
name: 'keyword.control.rdbms',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Real simple-mode commands — gets the `> ` prompt (via CSS). */
|
||||||
|
export default {
|
||||||
|
name: 'rdbms',
|
||||||
|
scopeName: 'source.rdbms',
|
||||||
|
aliases: ['rdbms-playground'],
|
||||||
|
patterns,
|
||||||
|
repository,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Abstract syntax templates — same highlighting, no prompt. */
|
||||||
|
export const rdbmsSyntax = {
|
||||||
|
name: 'rdbms-syntax',
|
||||||
|
scopeName: 'source.rdbms-syntax',
|
||||||
|
patterns,
|
||||||
|
repository,
|
||||||
|
};
|
||||||
@@ -2,3 +2,26 @@
|
|||||||
@import '@astrojs/starlight-tailwind';
|
@import '@astrojs/starlight-tailwind';
|
||||||
@import 'tailwindcss/theme.css' layer(theme);
|
@import 'tailwindcss/theme.css' layer(theme);
|
||||||
@import 'tailwindcss/utilities.css' layer(utilities);
|
@import 'tailwindcss/utilities.css' layer(utilities);
|
||||||
|
|
||||||
|
/* Command prompt for simple-mode (`rdbms`) code blocks: a decorative "> "
|
||||||
|
before each command line so consecutive commands read as distinct entered
|
||||||
|
lines (advanced-mode `sql` blocks are left unprefixed). It is CSS ::before
|
||||||
|
content with user-select:none, so it is NOT part of the copy buffer
|
||||||
|
(Expressive Code copies the block's `data-code`) and is never selected. */
|
||||||
|
[data-language='rdbms'] .ec-line .code::before {
|
||||||
|
content: '> ';
|
||||||
|
color: var(--sl-color-gray-3);
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The app's input field is single-line, so a multi-command copy can't be
|
||||||
|
pasted-and-run as one unit. Hide Expressive Code's copy button on
|
||||||
|
*multi-line* `rdbms` blocks (each line is a separate command — type them
|
||||||
|
one at a time). Single-command `rdbms` blocks keep copy, and `sql` / `sh`
|
||||||
|
blocks are unaffected (always one pasteable unit). Also hide it on
|
||||||
|
`rdbms-syntax` templates, which contain <placeholders> and aren't runnable. */
|
||||||
|
figure:has(> pre[data-language='rdbms'] .ec-line + .ec-line) .copy,
|
||||||
|
figure:has(> pre[data-language='rdbms-syntax']) .copy {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user