Some checks failed
Build & Test / Analyze (javascript, blocksuite) (push) Failing after 3s
Build & Test / Rust test filter (push) Failing after 3s
Build & Test / E2E BlockSuite Test (2) (push) Failing after 3s
Build & Test / E2E BlockSuite Test (3) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (4) (push) Failing after 3s
Build & Test / E2E BlockSuite Cross Browser Test (push) Failing after 3s
Build & Test / E2E Mobile Test (1) (push) Failing after 3s
Build & Test / E2E Mobile Test (2) (push) Failing after 3s
Build & Test / Build AFFiNE native (x86_64-unknown-linux-gnu) (push) Failing after 4s
Build & Test / Unit Test (1) (push) Has been skipped
Build & Test / Unit Test (2) (push) Has been skipped
Build & Test / Unit Test (3) (push) Has been skipped
Build & Test / Build Server native (push) Failing after 3s
Build & Test / Check Git Status (push) Has been skipped
Build & Test / Server Test (0, 4) (push) Has been skipped
Build & Test / Server Test (1, 4) (push) Has been skipped
Build & Test / Analyze (javascript, affine) (push) Failing after 3s
Build & Test / Analyze (typescript, affine) (push) Failing after 3s
Build & Test / Analyze (typescript, blocksuite) (push) Failing after 3s
Build & Test / Mobile native build filter (push) Failing after 3s
Build & Test / Check yarn binary (push) Failing after 3s
Build & Test / E2E BlockSuite Test (1) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (5) (push) Failing after 3s
Build & Test / Unit Test (4) (push) Has been skipped
Build & Test / Unit Test (5) (push) Has been skipped
Build & Test / Native Unit Test (push) Has been skipped
Build & Test / Server Test (2, 4) (push) Has been skipped
Build & Test / Server Test (3, 4) (push) Has been skipped
Build & Test / Server Test with Elasticsearch (push) Has been skipped
Build & Test / Server Test with Embedded Search (push) Has been skipped
Build & Test / Server Test with Manticore Search (push) Has been skipped
Build & Test / Server E2E Test (push) Has been skipped
Build & Test / Cloud Desktop E2E Test (push) Has been skipped
Build & Test / Cloud E2E Test 1/2 (push) Has been skipped
Build & Test / Cloud E2E Test 2/2 (push) Has been skipped
Build & Test / Build @affine/electron renderer (push) Failing after 3s
Build & Test / Copilot test filter (push) Failing after 4s
Build & Test / Lint Rust (push) Has been skipped
Build & Test / Build Android app (push) Has been skipped
Build & Test / Test Android app (push) Has been skipped
Build & Test / Run native tests (push) Has been skipped
Build & Test / Server Copilot Api Test (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (3, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (4, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (1, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (2, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (5, 5) (push) Has been skipped
Build & Test / Desktop Test (ubuntu-latest, linux, x64, x86_64-unknown-linux-gnu, true) (push) Has been cancelled
Build & Test / Desktop Test (windows-latest, windows, x64, x86_64-pc-windows-msvc, true) (push) Has been cancelled
Build & Test / 3, 2, 1 Launch (push) Has been cancelled
Build & Test / Lint (push) Has been cancelled
Build & Test / Typecheck (push) Has been cancelled
Build & Test / Build iOS app (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-apple-darwin) (push) Has been cancelled
Build & Test / E2E Test (1) (push) Has been cancelled
Build & Test / E2E Test (2) (push) Has been cancelled
Build & Test / E2E Test (3) (push) Has been cancelled
Build & Test / E2E Test (4) (push) Has been cancelled
Build & Test / E2E Test (5) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-apple-darwin) (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, arm64, aarch64-apple-darwin, true) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, x64, x86_64-apple-darwin, false) (push) Has been cancelled
191 lines
4.9 KiB
Markdown
191 lines
4.9 KiB
Markdown
# Command
|
|
|
|
Commands are the reusable actions for triggering state updates. Inside a command, you can query different states of the editor, or perform operations to update them. With the command API, you can define chainable commands and execute them.
|
|
|
|
## Command Chain
|
|
|
|
Commands are executed in a chain, and each command can decide whether to continue the chain or not.
|
|
|
|
```ts
|
|
std.command.chain().command1().command2().command3().run();
|
|
```
|
|
|
|
You will need to call `chain()` to start a new chain. Then, you can call any command defined in the `Commands` interface. And finally, call `run()` to execute the chain.
|
|
|
|
### Try
|
|
|
|
If a command fails, the chain will be interrupted. However, you can use `try()` to call a list of commands until one of them succeeds.
|
|
|
|
```ts
|
|
std.command
|
|
.chain()
|
|
.try(cmd => [cmd.command1(), cmd.command2()])
|
|
.command3()
|
|
.run();
|
|
```
|
|
|
|
In this chain, `command3` will be executed only if `command1` or `command2` succeeds. If `command1` succeeds, `command2` will not be executed.
|
|
|
|
### TryAll
|
|
|
|
`tryAll` is used to attempt to execute an array of commands within a chain. Unlike `try`, which stops executing the list of commands as soon as one of them succeeds, `tryAll` will execute every command in the array, regardless of the individual outcomes of each command.
|
|
|
|
This means that even if one of the commands succeeds, `tryAll` will still continue to execute the remaining commands in the array. The chain will only proceed to the next command after `tryAll` if at least one command in the array succeeds. If all commands fail, the chain will be interrupted.
|
|
|
|
```ts
|
|
std.command
|
|
.chain()
|
|
.tryAll(cmd => [cmd.command1(), cmd.command2(), cmd.command3()])
|
|
.command4()
|
|
.run();
|
|
```
|
|
|
|
If `command1`, `command2`, or `command3` succeeds, `command4` will be executed. If all commands in `tryAll` fail, the chain will stop, and `command4` will not be executed.
|
|
|
|
Use `tryAll` when you want to ensure that multiple strategies or operations are attempted, even if the success of one is enough to allow the chain to continue. This approach is useful when each command in the array should be given a chance to execute, regardless of the success of the others.
|
|
|
|
## Writing Commands
|
|
|
|
Commands are defined as pure functions.
|
|
|
|
```ts
|
|
import type { Command } from '@blocksuite/block-std';
|
|
export const myCommand: Command = (ctx, next) => {
|
|
if (fail) {
|
|
return;
|
|
}
|
|
|
|
return next();
|
|
};
|
|
|
|
declare global {
|
|
namespace BlockSuite {
|
|
interface Commands {
|
|
my: typeof myCommand;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add the command to the std command list
|
|
std.command.add('my', myCommand);
|
|
|
|
// You can call it with
|
|
std.command.chain().my().run();
|
|
```
|
|
|
|
Only when the command calls `next()`, the next command in the chain will be executed.
|
|
|
|
## Command Context
|
|
|
|
When a list of commands are executed, they share a context object.
|
|
This object is standalone for each command execution, and you can use it to store temporary data.
|
|
|
|
```ts
|
|
import type { Command } from '@blocksuite/block-std';
|
|
export const myCommand: Command<never, 'myCommandData'> = (ctx, next) => {
|
|
if (fail) {
|
|
return;
|
|
}
|
|
|
|
return next({ myCommandData: 'hello' });
|
|
};
|
|
|
|
declare global {
|
|
namespace BlockSuite {
|
|
interface CommandContext {
|
|
myCommandData: string;
|
|
}
|
|
|
|
interface Commands {
|
|
myCommand: typeof myCommand;
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Then, commands executed after `myCommand` can access the data:
|
|
|
|
```ts
|
|
export const myCommand: Command<'myCommandData'> = (ctx, next) => {
|
|
const data = ctx.myCommandData;
|
|
console.log(data);
|
|
};
|
|
```
|
|
|
|
## Command Options
|
|
|
|
You can pass options to a command when calling it:
|
|
|
|
```ts
|
|
import type { Command } from '@blocksuite/block-std';
|
|
|
|
type MyCommandOptions = {
|
|
configA: number;
|
|
configB: string;
|
|
};
|
|
export const myCommand: Command<never, never, MyCommandOptions> = (
|
|
ctx,
|
|
next
|
|
) => {
|
|
const { configA, configB } = ctx;
|
|
|
|
if (fail) {
|
|
return;
|
|
}
|
|
|
|
return next();
|
|
};
|
|
|
|
// You can call it with
|
|
std.command.chain().my({ configA: 0, configB: 'hello' }).run();
|
|
```
|
|
|
|
Please notice that commands take only one argument,
|
|
so you need to wrap the options in an object if you want to pass multiple options.
|
|
|
|
## Inline Command
|
|
|
|
You can also use inline command for some temporary commands.
|
|
|
|
```ts
|
|
std.command
|
|
.chain()
|
|
.inline((ctx, next) => {
|
|
// ...
|
|
return next();
|
|
})
|
|
.run();
|
|
```
|
|
|
|
## Command Returns
|
|
|
|
After `.run`, the command chain will return two values: `success` and `ctx`.
|
|
|
|
```ts
|
|
const [success, ctx] = std.command.chain().commandA().commandB().run();
|
|
```
|
|
|
|
If all commands passed, the `success` will be `true`, otherwise it will be `false`.
|
|
|
|
The `ctx` will be the final `context` updated by `.next` in a command chain.
|
|
|
|
For example:
|
|
|
|
```ts
|
|
const command1 = (ctx, next) => {
|
|
return next({ data: 0, str: 'hello' });
|
|
};
|
|
|
|
const command2 = (ctx, next) => {
|
|
return next({ data: 1 });
|
|
};
|
|
|
|
const [success, ctx] = std.command.chain().command1().command2().run();
|
|
|
|
// This will pass
|
|
expect(ctx.data).toBe(1);
|
|
|
|
// This will pass too
|
|
expect(ctx.str).toBe('hello');
|
|
```
|