Skip to content

Commit

Permalink
Support specifying attributes to DartPad (#5884)
Browse files Browse the repository at this point in the history
Enables the mechanism for adding a `title` attribute to DartPad code
blocks.

````markdown
```dartpad run="true" title="The classic hello world program in Dart!"
theme="dark"
void main() {
  print('Hello world!');
}
```
````

Contributes to #5251
  • Loading branch information
parlough authored May 30, 2024
1 parent 01e996b commit e0ff483
Show file tree
Hide file tree
Showing 11 changed files with 53 additions and 50 deletions.
11 changes: 7 additions & 4 deletions src/_11ty/plugins/highlight.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,16 @@ function _highlight(
language,
attributeString,
) {
const attributes = _parseAttributes(attributeString);

// Specially handle DartPad snippets so that inject_embed can convert them.
if (language.includes('-dartpad')) {
return `<pre><code data-dartpad="true" data-embed="true" data-theme="light">${markdown.utils.escapeHtml(content)}</code></pre>`;
if (language.includes('dartpad')) {
const theme = attributes['theme'] ?? 'light';
const title = attributes['title'] ?? 'Runnable Dart sample';
const runAutomatically = attributes['run'] ?? 'false';
return `<pre><code data-dartpad="true" data-embed="true" data-theme="${theme}" title="${title}" data-run="${runAutomatically}">${markdown.utils.escapeHtml(content)}</code></pre>`;
}

const attributes = _parseAttributes(attributeString);

const showLineNumbers = 'showLineNumbers' in attributes;
let startingLineNumber = 0;
if (showLineNumbers) {
Expand Down
16 changes: 8 additions & 8 deletions src/content/codelabs/async-await.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Before running this example, try to spot the issue --
what do you think the output will be?

<?code-excerpt "async_await/bin/get_order_sync_bad.dart" remove="Fetching"?>
```dart:run-dartpad:height-380px:ga_id-incorrect_usage
```dartpad
// This example shows how *not* to write asynchronous Dart code.
String createOrderMessage() {
Expand Down Expand Up @@ -170,7 +170,7 @@ try to predict which will print first:
"Large Latte" or "Fetching user order...".

<?code-excerpt "async_await/bin/futures_intro.dart (no-error)"?>
```dart:run-dartpad:height-300px:ga_id-introducting_futures
```dartpad
Future<void> fetchUserOrder() {
// Imagine that this function is fetching user info from another service or database.
return Future.delayed(const Duration(seconds: 2), () => print('Large Latte'));
Expand All @@ -194,7 +194,7 @@ Run the following example to see how a future completes with an error.
A bit later you'll learn how to handle the error.

<?code-excerpt "async_await/bin/futures_intro.dart (error)" replace="/Error//g"?>
```dart:run-dartpad:height-300px:ga_id-completing_with_error
```dartpad
Future<void> fetchUserOrder() {
// Imagine that this function is fetching user info but encounters a bug.
return Future.delayed(
Expand Down Expand Up @@ -375,7 +375,7 @@ within an `async` function body.
What do you think the output will be?

<?code-excerpt "async_await/bin/async_example.dart" remove="/\/\/ print/"?>
```dart:run-dartpad:height-530px:ga_id-execution_within_async_function
```dartpad
Future<void> printOrderMessage() async {
print('Awaiting user order...');
var order = await fetchUserOrder();
Expand Down Expand Up @@ -457,7 +457,7 @@ Implement an `async` function `reportLogins()` so that it does the following:
* Example return value from `reportLogins()`: `"Total number of logins: 57"`
* Gets the number of logins by calling the provided function `fetchLoginAmount()`.

```dart:run-dartpad:theme-dark:height-380px:ga_id-practice_using
```dartpad theme="dark"
// Part 1
// Call the provided async function fetchRole()
// to return the user role.
Expand Down Expand Up @@ -640,7 +640,7 @@ from an asynchronous function.
What do you think the output will be?

<?code-excerpt "async_await/bin/try_catch.dart"?>
```dart:run-dartpad:height-530px:ga_id-try_catch
```dartpad
Future<void> printOrderMessage() async {
try {
print('Awaiting user order...');
Expand Down Expand Up @@ -693,7 +693,7 @@ that does the following:
and
[Errors.]({{site.dart-api}}/{{site.sdkInfo.channel}}/dart-core/Error-class.html)

```dart:run-dartpad:theme-dark:height-380px:ga_id-practice_errors
```dartpad theme="dark"
// TODO: Implement changeUsername here.
changeUsername() {}
Expand Down Expand Up @@ -861,7 +861,7 @@ Write the following:
`'<result> Thanks, see you next time'`, where `<result>` is
the string value returned by calling `logoutUser()`.

```dart:run-dartpad:theme-dark:height-380px:ga_id-putting_it_all_together
```dartpad theme="dark"
// Part 1
addHello(String user) {}
Expand Down
34 changes: 17 additions & 17 deletions src/content/codelabs/dart-cheatsheet.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ The following function takes two integers as parameters.
Make it return a string containing both integers separated by a space.
For example, `stringify(2, 3)` should return `'2 3'`.

```dart:run-dartpad:ga_id-string_interpolation
```dartpad
String stringify(int x, int y) {
TODO('Return a formatted string here');
}
Expand Down Expand Up @@ -123,7 +123,7 @@ Try to declare two variables below:

Ignore all initial errors in the DartPad.

```dart:run-dartpad:ga_id-nullable_variables
```dartpad
// TODO: Declare the two variables here
Expand Down Expand Up @@ -192,7 +192,7 @@ to implement the described behavior in the following snippet.

Ignore all initial errors in the DartPad.

```dart:run-dartpad:height-255px:ga_id-null_aware
```dartpad
String? foo = 'a string';
String? bar; // = null
Expand Down Expand Up @@ -282,7 +282,7 @@ The following function takes a nullable string as a parameter.
Try using conditional property access to make it
return the uppercase version of `str`, or `null` if `str` is `null`.

```dart:run-dartpad:ga_id-conditional-property_access
```dartpad
String? upperCaseIt(String? str) {
// TODO: Try conditionally accessing the `toUpperCase` method here.
}
Expand Down Expand Up @@ -372,7 +372,7 @@ final aListOfBaseType = <BaseType>[SubType(), SubType()];

Try setting the following variables to the indicated values. Replace the existing null values.

```dart:run-dartpad:height-400px:ga_id-collection_literals
```dartpad
// Assign this a list containing 'a', 'b', and 'c' in that order:
final aListOfStrings = null;
Expand Down Expand Up @@ -502,7 +502,7 @@ bool hasEmpty = aListOfStrings.any((s) => s.isEmpty);

Try finishing the following statements, which use arrow syntax.

```dart:run-dartpad:height-345px:ga_id-arrow_syntax
```dartpad
class MyClass {
int value1 = 2;
int value2 = 3;
Expand Down Expand Up @@ -654,7 +654,7 @@ sets the `anInt`, `aString`, and `aList` properties of a `BigObject`
to `1`, `'String!'`, and `[3.0]` (respectively)
and then calls `allDone()`.

```dart:run-dartpad:height-345px:ga_id-cascades
```dartpad
class BigObject {
int anInt = 0;
String aString = '';
Expand Down Expand Up @@ -791,7 +791,7 @@ Add the following:

Ignore all initial errors in the DartPad.

```dart:run-dartpad:height-240px:ga_id-getters_setters
```dartpad
class InvalidPriceException {}
class ShoppingCart {
Expand Down Expand Up @@ -948,7 +948,7 @@ Here are some examples of function calls and returned values:

<br>

```dart:run-dartpad:ga_id-optional_positional_parameters
```dartpad
String joinWithCommas(int a, [int? b, int? c, int? d, int? e]) {
return TODO();
}
Expand Down Expand Up @@ -1090,7 +1090,7 @@ then copy its value into `anInt`.

Ignore all initial errors in the DartPad.

```dart:run-dartpad:height-310px:ga_id-optional_named_parameters
```dartpad
class MyDataObject {
final int anInt;
final String aString;
Expand Down Expand Up @@ -1258,7 +1258,7 @@ then do the following:
* After everything's caught and handled, call `logger.doneLogging`
(try using `finally`).

```dart:run-dartpad:height-420px:ga_id-exceptions
```dartpad
typedef VoidFunction = void Function();
class ExceptionWithMessage {
Expand Down Expand Up @@ -1453,7 +1453,7 @@ all three properties of the class.

Ignore all initial errors in the DartPad.

```dart:run-dartpad:ga_id-this_constructor
```dartpad
class MyClass {
final int anInt;
final String aString;
Expand Down Expand Up @@ -1568,7 +1568,7 @@ FINALLY: Suggest using https://pub.dev/packages/characters
if this is a user-entered string.
{% endcomment %}

```dart:run-dartpad:ga_id-initializer_lists
```dartpad
class FirstTwoLetters {
final String letterOne;
final String letterTwo;
Expand Down Expand Up @@ -1670,7 +1670,7 @@ that sets all three properties to zero.

Ignore all initial errors in the DartPad.

```dart:run-dartpad:height-240px:ga_id-named_constructors
```dartpad
class Color {
int red;
int green;
Expand Down Expand Up @@ -1766,7 +1766,7 @@ making it do the following:
create an `IntegerTriple` with the values in order.
* Otherwise, throw an `Error`.

```dart:run-dartpad:height-415px:ga_id-factory_constructors
```dartpad
class IntegerHolder {
IntegerHolder();
Expand Down Expand Up @@ -1937,7 +1937,7 @@ default constructor with zeros as the arguments.

Ignore all initial errors in the DartPad.

```dart:run-dartpad:height-255px:ga_id-redirecting_constructors
```dartpad
class Color {
int red;
int green;
Expand Down Expand Up @@ -2024,7 +2024,7 @@ and create a constant constructor that does the following:

Ignore all initial errors in the DartPad.

```dart:run-dartpad:ga_id-const_constructors
```dartpad
class Recipe {
List<String> ingredients;
int calories;
Expand Down
24 changes: 12 additions & 12 deletions src/content/codelabs/iterables.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ using a `for-in` loop.
The following example shows you how to read elements using a `for-in` loop.

<?code-excerpt "iterables/test/iterables_test.dart (for-in)"?>
```dart:run-dartpad:ga_id-for_in_loop
```dartpad
void main() {
const iterable = ['Salad', 'Popcorn', 'Toast'];
for (final element in iterable) {
Expand Down Expand Up @@ -173,7 +173,7 @@ results in a [StateError.][StateError class]
:::

<?code-excerpt "iterables/test/iterables_test.dart (first-last)"?>
```dart:run-dartpad:ga_id-first_and_last
```dartpad
void main() {
Iterable<String> iterable = const ['Salad', 'Popcorn', 'Toast'];
print('The first element is ${iterable.first}');
Expand Down Expand Up @@ -211,7 +211,7 @@ Run the following example to see how `firstWhere()` works.
Do you think all the functions will give the same result?

<?code-excerpt "iterables/test/iterables_test.dart (first-where-long)"?>
```dart:run-dartpad:height-565px:ga_id-using_firstwhere
```dartpad
bool predicate(String item) {
return item.length > 5;
}
Expand Down Expand Up @@ -313,7 +313,7 @@ satisfies the following conditions:
All the elements in the test data are [strings][String class];
you can check the class documentation for help.

```dart:run-dartpad:theme-dark:ga_id-practice_writing_a_test_predicate
```dartpad theme="dark"
// Implement the predicate of singleWhere
// with the following conditions
// * The element contains the character `'a'`
Expand Down Expand Up @@ -421,7 +421,7 @@ you can use to verify conditions:
Run this exercise to see them in action.

<?code-excerpt "iterables/test/iterables_test.dart (any-every)"?>
```dart:run-dartpad:height-255px:ga_id-using_any_and_every
```dartpad
void main() {
const items = ['Salad', 'Popcorn', 'Toast'];
Expand Down Expand Up @@ -470,7 +470,7 @@ Use `any()` and `every()` to implement two functions:
* Part 2: Implement `everyUserOver13()`.
* Return `true` if all users are 14 or older.

```dart:run-dartpad:theme-dark:height-395px:ga_id-verify_iterable
```dartpad
bool anyUserUnder18(Iterable<User> users) {
// TODO: Implement the anyUserUnder18 function.
}
Expand Down Expand Up @@ -660,7 +660,7 @@ Run this example to see how `where()` can be used together with other
methods like `any()`.

<?code-excerpt "iterables/test/iterables_test.dart (numbers-where)"?>
```dart:run-dartpad:height-380px:ga_id-using_where
```dartpad
void main() {
var evenNumbers = const [1, -2, 3, 42].where((number) => number.isEven);
Expand Down Expand Up @@ -703,7 +703,7 @@ Run this example to see how `takeWhile()` and `skipWhile()` can
split an `Iterable` containing numbers.

<?code-excerpt "iterables/test/iterables_test.dart (take-while-long)"?>
```dart:run-dartpad:ga_id-using_takewhile
```dartpad
void main() {
const numbers = [1, 3, -2, 0, 4, 5];
Expand Down Expand Up @@ -747,7 +747,7 @@ Use `where()` to implement two functions:
* Return an `Iterable` containing all users with
names of length 3 or less.

```dart:run-dartpad:theme-dark:height-380px:ga_id-filtering_elements_from_a_list
```dartpad theme="dark"
Iterable<User> filterOutUnder21(Iterable<User> users) {
// TODO: Implement the filterOutUnder21 function.
}
Expand Down Expand Up @@ -893,7 +893,7 @@ multiply all the elements of an `Iterable` by 2.
What do you think the output will be?

<?code-excerpt "iterables/test/iterables_test.dart (numbers-by-two)"?>
```dart:run-dartpad:ga_id-using_map
```dartpad
void main() {
var numbersByTwo = const [1, -2, 3, 42].map((number) => number * 2);
print('Numbers: $numbersByTwo');
Expand All @@ -913,7 +913,7 @@ contains strings containing each user's name and age.
Each string in the `Iterable` must follow this format:
`'{name} is {age}'`—for example `'Alice is 21'`.

```dart:run-dartpad:theme-dark:height-310px:ga_id-mapping_to_a_different_type
```dartpad theme="dark"
Iterable<String> getNameAndAges(Iterable<User> users) {
// TODO: Implement the getNameAndAges function.
}
Expand Down Expand Up @@ -1043,7 +1043,7 @@ Part 3: Implement `validEmailAddresses()`.
- Use the provided function `isValidEmailAddress()` to evaluate whether
an `EmailAddress` is valid.

```dart:run-dartpad:theme-dark:height-600px:ga_id-putting_it_all_together
```dartpad theme="dark"
Iterable<EmailAddress> parseEmailAddresses(Iterable<String> strings) {
// TODO: Implement the parseEmailAddresses function.
}
Expand Down
2 changes: 1 addition & 1 deletion src/content/language/callable-objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ that takes three strings and concatenates them, separating each with a space,
and appending an exclamation. Click **Run** to execute the code.

<?code-excerpt "misc/lib/language_tour/callable_objects.dart"?>
```dart:run-dartpad:height-350px:ga_id-callable_objects
```dartpad
class WannabeFunction {
String call(String a, String b, String c) => '$a $b $c!';
}
Expand Down
4 changes: 2 additions & 2 deletions src/content/language/constructors.md
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ The following example initializes three `final` fields in an initializer list.
To execute the code, click **Run**.

<?code-excerpt "point_with_distance_field.dart"?>
```dart:run-dartpad:height-340px:ga_id-initializer_list
```dartpad
import 'dart:math';
class Point {
Expand Down Expand Up @@ -484,7 +484,7 @@ the `Employee` class constructor calls the named constructor
for its superclass, `Person`. To execute the following code, click **Run**.

<?code-excerpt "employee.dart (super)" plaster="none"?>
```dart:run-dartpad:height-450px:ga_id-non_default_superclass_constructor
```dartpad
class Person {
String? firstName;
Expand Down
2 changes: 1 addition & 1 deletion src/content/language/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ for (var item in uppercaseList) {
Click **Run** to execute the code.

<?code-excerpt "misc/test/language_tour/functions_test.dart (anonymous-function-main)"?>
```dart:run-dartpad:height-400px:ga_id-anonymous_functions
```dartpad
void main() {
const list = ['apples', 'bananas', 'oranges'];
Expand Down
Loading

0 comments on commit e0ff483

Please sign in to comment.