Skip to content

Commit

Permalink
feat(prompt): add id option to automatically save suggestions to loca…
Browse files Browse the repository at this point in the history
…l storage (#204)
  • Loading branch information
c4spar committed May 20, 2021
1 parent a965ea3 commit 28f25bd
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 40 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test-nightly.yml
Expand Up @@ -24,4 +24,4 @@ jobs:
deno-version: ${{ matrix.deno_version }}

- name: Run tests
run: deno test --allow-all --unstable
run: deno test --allow-all --unstable --location https://cliffy.io
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Expand Up @@ -29,7 +29,7 @@ jobs:
if: ${{ matrix.deno != 'v1.x' }}

- name: Run tests
run: deno test --coverage=./cov --allow-all --unstable
run: deno test --coverage=./cov --allow-all --unstable --location https://cliffy.io
if: ${{ matrix.deno == 'v1.x' }}

- name: Generate lcov
Expand Down
97 changes: 61 additions & 36 deletions prompt/README.md
Expand Up @@ -410,8 +410,18 @@ $ deno run --unstable https://deno.land/x/cliffy/examples/prompt/input.ts

#### Auto suggestions

You can add suggestions to the `Input`, `Number` and `List` prompt to enable
tab-completions.
You can provide suggestions to the `Input`, `Number` and `List` prompt to enable
tab-completions with the `suggestions` and/or `id` option. If an `id` is
provided, the values will be saved to the local storage using the `id` as local
storage key. Both options can be defined at the same time.

The `id` option requires deno >= `1.10` and the `--location` flag.

```shell
deno install you/cli.ts --location https://example.com
# or
deno run you/cli.ts --location https://example.com
```

![](assets/img/suggestions.gif)

Expand All @@ -420,6 +430,7 @@ import { Input } from "https://deno.land/x/cliffy/prompt/input.ts";

const color: string = await Input.prompt({
message: "Choose a color",
id: "<local-storage-key>",
suggestions: [
"Abbey",
"Absolute Zero",
Expand Down Expand Up @@ -482,15 +493,16 @@ $ deno run --unstable https://deno.land/x/cliffy/examples/prompt/suggestions_lis
The `Input` prompt has all [base](#base-options) and the following prompt
specific options.

| Param | Type | Required | Description |
| ----------- | :-----------------------: | :------: | --------------------------------------------------------- |
| minLength | `number` | No | Min length of value. Defaults to `0`. |
| maxLength | `number` | No | Max length of value. Defaults to `infinity`. |
| suggestions | `Array<string \| number>` | No | A list of auto suggestions. |
| list | `number` | No | Show auto suggestions list. |
| maxRows | `number` | No | Number of options suggestions per page. Defaults to `10`. |
| listPointer | `string` | No | Change the list pointer icon. |
| info | `number` | No | Show some usage information. |
| Param | Type | Required | Description |
| ----------- | :-----------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| minLength | `number` | No | Min length of value. Defaults to `0`. |
| maxLength | `number` | No | Max length of value. Defaults to `infinity`. |
| suggestions | `Array<string \| number>` | No | A list of auto suggestions. |
| id | `string` | No | If an id is provided, values are stored in the local storage using the id as local storage key. The stored values are used as suggestions at the next time the prompt is used. |
| list | `number` | No | Show auto suggestions list. |
| maxRows | `number` | No | Number of options suggestions per page. Defaults to `10`. |
| listPointer | `string` | No | Change the list pointer icon. |
| info | `number` | No | Show some usage information. |

**↑ back to:** [Prompt types](#-types)

Expand All @@ -517,17 +529,18 @@ $ deno run --unstable https://deno.land/x/cliffy/examples/prompt/number.ts
The `Number` prompt has all [base options](#base-options) and the following
prompt specific options.

| Param | Type | Required | Description |
| ----------- | :-----------------------: | :------: | --------------------------------------------------------- |
| min | `number` | No | Min value. Defaults to `-infinity`. |
| max | `number` | No | Max value. Defaults to `Infinity`. |
| float | `boolean` | No | Allow floating point inputs. Defaults to `false`. |
| round | `number` | No | Round float values to `x` decimals. Defaults to `2`. |
| suggestions | `Array<string \| number>` | No | A list of auto suggestions. |
| list | `number` | No | Show auto suggestions list. |
| maxRows | `number` | No | Number of options suggestions per page. Defaults to `10`. |
| listPointer | `string` | No | Change the list pointer icon. |
| info | `number` | No | Show some usage information. |
| Param | Type | Required | Description |
| ----------- | :-----------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| min | `number` | No | Min value. Defaults to `-infinity`. |
| max | `number` | No | Max value. Defaults to `Infinity`. |
| float | `boolean` | No | Allow floating point inputs. Defaults to `false`. |
| round | `number` | No | Round float values to `x` decimals. Defaults to `2`. |
| suggestions | `Array<string \| number>` | No | A list of auto suggestions. |
| id | `string` | No | If an id is provided, values are stored in the local storage using the id as local storage key. The stored values are used as suggestions at the next time the prompt is used. |
| list | `number` | No | Show auto suggestions list. |
| maxRows | `number` | No | Number of options suggestions per page. Defaults to `10`. |
| listPointer | `string` | No | Change the list pointer icon. |
| info | `number` | No | Show some usage information. |

**↑ back to:** [Prompt types](#-types)

Expand Down Expand Up @@ -643,8 +656,18 @@ $ deno run --unstable https://deno.land/x/cliffy/examples/prompt/list.ts

#### Auto suggestions

You can add suggestions to the `Input`, `Number` and `List` prompt to enable
tab-completions.
You can provide suggestions to the `Input`, `Number` and `List` prompt to enable
tab-completions with the `suggestions` and/or `id` option. If an `id` is
provided, the value will be saved to the local storage using the `id` as local
storage key. Both options can be defined at the same time.

The `id` option requires deno >= `1.10` and the `--location` flag.

```shell
deno install you/cli.ts --location https://example.com
# or
deno run you/cli.ts --location https://example.com
```

![](assets/img/suggestions_list_prompt.gif)

Expand All @@ -653,6 +676,7 @@ import { List } from "https://deno.land/x/cliffy/prompt/list.ts";

const color: string = await List.prompt({
message: "Choose a color",
id: "<local-storage-key>",
suggestions: [
"Abbey",
"Absolute Zero",
Expand All @@ -679,18 +703,19 @@ $ deno run --unstable https://deno.land/x/cliffy/examples/prompt/suggestions_lis
The `List` prompt has all [base options](#base-options) and the following prompt
specific options.

| Param | Type | Required | Description |
| ----------- | :-----------------------: | :------: | --------------------------------------------------------------------------------------------- |
| separator | `string` | No | String separator. Will trim all white-spaces from start and end of string. Defaults to `','`. |
| minLength | `number` | No | Min length of a single tag. Defaults to `0`. |
| maxLength | `number` | No | Max length of a single tag. Defaults to `infinity`. |
| minTags | `number` | No | Min number of tags. Defaults to `0`. |
| maxTags | `number` | No | Max number of tags. Defaults to `infinity`. |
| suggestions | `Array<string \| number>` | No | A list of auto suggestions. |
| list | `number` | No | Show auto suggestions list. |
| maxRows | `number` | No | Number of options suggestions per page. Defaults to `10`. |
| listPointer | `string` | No | Change the list pointer icon. |
| info | `number` | No | Show some usage information. |
| Param | Type | Required | Description |
| ----------- | :-----------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| separator | `string` | No | String separator. Will trim all white-spaces from start and end of string. Defaults to `','`. |
| minLength | `number` | No | Min length of a single tag. Defaults to `0`. |
| maxLength | `number` | No | Max length of a single tag. Defaults to `infinity`. |
| minTags | `number` | No | Min number of tags. Defaults to `0`. |
| maxTags | `number` | No | Max number of tags. Defaults to `infinity`. |
| suggestions | `Array<string \| number>` | No | A list of auto suggestions. |
| id | `string` | No | If an id is provided, values are stored in the local storage using the id as local storage key. The stored values are used as suggestions at the next time the prompt is used. |
| list | `number` | No | Show auto suggestions list. |
| maxRows | `number` | No | Number of options suggestions per page. Defaults to `10`. |
| listPointer | `string` | No | Change the list pointer icon. |
| info | `number` | No | Show some usage information. |

**↑ back to:** [Prompt types](#-types)

Expand Down
55 changes: 53 additions & 2 deletions prompt/_generic_suggestions.ts
Expand Up @@ -9,6 +9,18 @@ import { blue, bold, dim, stripColor, underline } from "./deps.ts";
import { Figures } from "./figures.ts";
import { distance } from "../_utils/distance.ts";

interface LocalStorage {
getItem(key: string): string | null;
removeItem(key: string): void;
setItem(key: string, value: string): void;
}

// keep support for deno < 1.10
const localStorage: LocalStorage | null = "localStorage" in window
? // deno-lint-ignore no-explicit-any
(window as any).localStorage
: null;

/** Input keys options. */
export interface GenericSuggestionsKeys extends GenericInputKeys {
complete?: string[];
Expand All @@ -22,6 +34,7 @@ export interface GenericSuggestionsKeys extends GenericInputKeys {
export interface GenericSuggestionsOptions<T, V>
extends GenericInputPromptOptions<T, V> {
keys?: GenericSuggestionsKeys;
id?: string;
suggestions?: Array<string | number>;
list?: boolean;
info?: boolean;
Expand All @@ -33,6 +46,7 @@ export interface GenericSuggestionsOptions<T, V>
export interface GenericSuggestionsSettings<T, V>
extends GenericInputPromptSettings<T, V> {
keys?: GenericSuggestionsKeys;
id?: string;
suggestions?: Array<string | number>;
list?: boolean;
info?: boolean;
Expand Down Expand Up @@ -66,8 +80,36 @@ export abstract class GenericSuggestions<
...(settings.keys ?? {}),
},
});
if (this.settings.suggestions) {
this.settings.suggestions = this.settings.suggestions.slice();
const suggestions: Array<string | number> = this.loadSuggestions();
if (suggestions.length || this.settings.suggestions) {
this.settings.suggestions = [
...suggestions,
...this.settings.suggestions ?? [],
].filter(uniqueSuggestions);
}
}

protected loadSuggestions(): Array<string | number> {
if (this.settings.id) {
const json = localStorage?.getItem(this.settings.id);
const suggestions: Array<string | number> = json ? JSON.parse(json) : [];
if (!Array.isArray(suggestions)) {
return [];
}
return suggestions;
}
return [];
}

protected saveSuggestions(...suggestions: Array<string | number>): void {
if (this.settings.id) {
localStorage?.setItem(
this.settings.id,
JSON.stringify([
...suggestions,
...this.loadSuggestions(),
].filter(uniqueSuggestions)),
);
}
}

Expand Down Expand Up @@ -342,3 +384,12 @@ export abstract class GenericSuggestions<
}
}
}

function uniqueSuggestions(
value: unknown,
index: number,
self: Array<unknown>,
) {
return typeof value !== "undefined" && value !== "" &&
self.indexOf(value) === index;
}
5 changes: 5 additions & 0 deletions prompt/confirm.ts
Expand Up @@ -81,6 +81,11 @@ export class Confirm
return defaultMessage ? dim(` (${defaultMessage})`) : "";
}

protected success(value: boolean): string | undefined {
this.saveSuggestions(this.format(value));
return super.success(value);
}

/** Get input input. */
protected getValue(): string {
return this.inputValue;
Expand Down
5 changes: 5 additions & 0 deletions prompt/input.ts
Expand Up @@ -52,6 +52,11 @@ export class Input extends GenericSuggestions<string, string, InputSettings> {
GenericPrompt.inject(value);
}

protected success(value: string): string | undefined {
this.saveSuggestions(value);
return super.success(value);
}

/** Get input input. */
protected getValue(): string {
return this.inputValue;
Expand Down
5 changes: 5 additions & 0 deletions prompt/list.ts
Expand Up @@ -90,6 +90,11 @@ export class List extends GenericSuggestions<string[], string, ListSettings> {
);
}

protected success(value: string[]): string | undefined {
this.saveSuggestions(...value);
return super.success(value);
}

/** Get input value. */
protected getValue(): string {
// Remove trailing comma and spaces.
Expand Down
5 changes: 5 additions & 0 deletions prompt/number.ts
Expand Up @@ -69,6 +69,11 @@ export class Number extends GenericSuggestions<number, string, NumberSettings> {
GenericPrompt.inject(value);
}

protected success(value: number): string | undefined {
this.saveSuggestions(value);
return super.success(value);
}

/**
* Handle user input event.
* @param event Key event.
Expand Down

0 comments on commit 28f25bd

Please sign in to comment.