From f9835b7d38a288793b789f4858c06d8812be4906 Mon Sep 17 00:00:00 2001 From: Jason Dent Date: Sat, 5 Feb 2022 14:11:17 +0100 Subject: [PATCH] fix: Add simple repl feature to suggestions. (#2403) --- packages/cspell/package-lock.json | 72 ++++++++++++++---------- packages/cspell/src/application.ts | 9 ++- packages/cspell/src/commandSuggestion.ts | 3 +- packages/cspell/src/options.ts | 5 ++ packages/cspell/src/repl/index.ts | 59 +++++++++++++++++++ packages/cspell/tsconfig.json | 1 + 6 files changed, 116 insertions(+), 33 deletions(-) create mode 100644 packages/cspell/src/repl/index.ts diff --git a/packages/cspell/package-lock.json b/packages/cspell/package-lock.json index 4786091db80..df6e0a80818 100644 --- a/packages/cspell/package-lock.json +++ b/packages/cspell/package-lock.json @@ -54,13 +54,13 @@ } }, "node_modules/@ampproject/remapping": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.0.2.tgz", - "integrity": "sha512-sE8Gx+qSDMLoJvb3QarJJlDQK7SSY4rK3hxp4XsiANeFOmjU46ZI7Y9adAQRJrmbz8zbtZkp3mJTT+rGxtF0XA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.0.3.tgz", + "integrity": "sha512-DmIAguV77yFP0MGVFWknCMgSLAtsLR3VlRTteR6xgMpIfYtwaZuMvjGv5YlpiqN7S/5q87DHyuIx8oa15kiyag==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.2.2", - "sourcemap-codec": "1.4.8" + "@jridgewell/sourcemap-codec": "^1.4.9", + "@jridgewell/trace-mapping": "^0.2.7" }, "engines": { "node": ">=6.0.0" @@ -1218,14 +1218,20 @@ "node": ">=6.0.0" } }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.9.tgz", + "integrity": "sha512-iKsUDLGOrC5pSdVTyb8zJI/f55wItTzGtfGWiWPWTc8h2P4oucax7XOGSRq9V2aA1nwE8qMaGvwdXk3PZRtgjg==", + "dev": true + }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.2.6.tgz", - "integrity": "sha512-rVJf5dSMEBxnDEwtAT5x8+p6tZ+xU6Ocm+cR1MYL2gMsRi4MMzVf9Pvq6JaxIsEeKAyYmo2U+yPQN4QfdTfFnA==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.2.7.tgz", + "integrity": "sha512-ZKfRhw6eK2vvdWqpU7DQq49+BZESqh5rmkYpNhuzkz01tapssl2sNNy6uMUIgrTtUWQDijomWJzJRCoevVrfgw==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", - "sourcemap-codec": "1.4.8" + "@jridgewell/sourcemap-codec": "^1.4.9" } }, "node_modules/@sinonjs/commons": { @@ -1385,9 +1391,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "17.0.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.14.tgz", - "integrity": "sha512-SbjLmERksKOGzWzPNuW7fJM7fk3YXVTFiZWB/Hs99gwhk+/dnrQRPBQjPW9aO+fi1tAffi9PrwFvsmOKmDTyng==", + "version": "17.0.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.15.tgz", + "integrity": "sha512-zWt4SDDv1S9WRBNxLFxFRHxdD9tvH8f5/kg5/IaLFdnSNXsDY4eL3Q3XXN+VxUnWIhyVFDwcsmAprvwXoM/ClA==", "dev": true }, "node_modules/@types/parse-json": { @@ -2164,9 +2170,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.64", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.64.tgz", - "integrity": "sha512-8mec/99xgLUZCIZZq3wt61Tpxg55jnOSpxGYapE/1Ma9MpFEYYaz4QNYm0CM1rrnCo7i3FRHhbaWjeCLsveGjQ==", + "version": "1.4.65", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.65.tgz", + "integrity": "sha512-0/d8Skk8sW3FxXP0Dd6MnBlrwx7Qo9cqQec3BlIAlvKnrmS3pHsIbaroEi+nd0kZkGpQ6apMEre7xndzjlEnLw==", "dev": true }, "node_modules/emittery": { @@ -4910,13 +4916,13 @@ }, "dependencies": { "@ampproject/remapping": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.0.2.tgz", - "integrity": "sha512-sE8Gx+qSDMLoJvb3QarJJlDQK7SSY4rK3hxp4XsiANeFOmjU46ZI7Y9adAQRJrmbz8zbtZkp3mJTT+rGxtF0XA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.0.3.tgz", + "integrity": "sha512-DmIAguV77yFP0MGVFWknCMgSLAtsLR3VlRTteR6xgMpIfYtwaZuMvjGv5YlpiqN7S/5q87DHyuIx8oa15kiyag==", "dev": true, "requires": { - "@jridgewell/trace-mapping": "^0.2.2", - "sourcemap-codec": "1.4.8" + "@jridgewell/sourcemap-codec": "^1.4.9", + "@jridgewell/trace-mapping": "^0.2.7" } }, "@babel/code-frame": { @@ -5851,14 +5857,20 @@ "integrity": "sha512-cz8HFjOFfUBtvN+NXYSFMHYRdxZMaEl0XypVrhzxBgadKIXhIkRd8aMeHhmF56Sl7SuS8OnUpQ73/k9LE4VnLg==", "dev": true }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.9.tgz", + "integrity": "sha512-iKsUDLGOrC5pSdVTyb8zJI/f55wItTzGtfGWiWPWTc8h2P4oucax7XOGSRq9V2aA1nwE8qMaGvwdXk3PZRtgjg==", + "dev": true + }, "@jridgewell/trace-mapping": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.2.6.tgz", - "integrity": "sha512-rVJf5dSMEBxnDEwtAT5x8+p6tZ+xU6Ocm+cR1MYL2gMsRi4MMzVf9Pvq6JaxIsEeKAyYmo2U+yPQN4QfdTfFnA==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.2.7.tgz", + "integrity": "sha512-ZKfRhw6eK2vvdWqpU7DQq49+BZESqh5rmkYpNhuzkz01tapssl2sNNy6uMUIgrTtUWQDijomWJzJRCoevVrfgw==", "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", - "sourcemap-codec": "1.4.8" + "@jridgewell/sourcemap-codec": "^1.4.9" } }, "@sinonjs/commons": { @@ -6015,9 +6027,9 @@ "dev": true }, "@types/node": { - "version": "17.0.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.14.tgz", - "integrity": "sha512-SbjLmERksKOGzWzPNuW7fJM7fk3YXVTFiZWB/Hs99gwhk+/dnrQRPBQjPW9aO+fi1tAffi9PrwFvsmOKmDTyng==", + "version": "17.0.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.15.tgz", + "integrity": "sha512-zWt4SDDv1S9WRBNxLFxFRHxdD9tvH8f5/kg5/IaLFdnSNXsDY4eL3Q3XXN+VxUnWIhyVFDwcsmAprvwXoM/ClA==", "dev": true }, "@types/parse-json": { @@ -6624,9 +6636,9 @@ } }, "electron-to-chromium": { - "version": "1.4.64", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.64.tgz", - "integrity": "sha512-8mec/99xgLUZCIZZq3wt61Tpxg55jnOSpxGYapE/1Ma9MpFEYYaz4QNYm0CM1rrnCo7i3FRHhbaWjeCLsveGjQ==", + "version": "1.4.65", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.65.tgz", + "integrity": "sha512-0/d8Skk8sW3FxXP0Dd6MnBlrwx7Qo9cqQec3BlIAlvKnrmS3pHsIbaroEi+nd0kZkGpQ6apMEre7xndzjlEnLw==", "dev": true }, "emittery": { diff --git a/packages/cspell/src/application.ts b/packages/cspell/src/application.ts index c4fd8586218..6fcf7729d1a 100644 --- a/packages/cspell/src/application.ts +++ b/packages/cspell/src/application.ts @@ -1,3 +1,4 @@ +import * as async from '@cspell/cspell-pipe'; import type { CSpellReporter, RunResult } from '@cspell/cspell-types'; import type { CheckTextInfo, SuggestionsForWordResult, TraceResult } from 'cspell-lib'; import { @@ -12,7 +13,7 @@ import { import * as path from 'path'; import { LintRequest, runLint } from './lint'; import { BaseOptions, fixLegacy, LegacyOptions, LinterOptions, SuggestionOptions, TraceOptions } from './options'; -import * as async from '@cspell/cspell-pipe'; +import { simpleRepl } from './repl'; import { calcFinalConfigInfo, readConfig, readFile } from './util/fileHelper'; import { readStdin } from './util/stdin'; import * as util from './util/util'; @@ -56,7 +57,11 @@ export async function* suggestions( ): AsyncIterable { options = fixLegacy(options); const configFile = await readConfig(options.config, undefined); - const iWords = options.useStdin ? async.toAsyncIterable(words, readStdin()) : words; + const iWords = options.repl + ? async.toAsyncIterable(words, simpleRepl()) + : options.useStdin + ? async.toAsyncIterable(words, readStdin()) + : words; try { const results = suggestionsForWords(iWords, options, configFile.config); yield* results; diff --git a/packages/cspell/src/commandSuggestion.ts b/packages/cspell/src/commandSuggestion.ts index 1b0396331e4..31bdda2532b 100644 --- a/packages/cspell/src/commandSuggestion.ts +++ b/packages/cspell/src/commandSuggestion.ts @@ -59,6 +59,7 @@ export function commandSuggestion(prog: Command): Command { 'Force the number of suggested to be limited, by not including suggestions that have the same edit cost.' ) .option('--stdin', 'Use stdin for input.') + .addOption(new CommanderOption('--repl', 'REPL interface for looking up suggestions.').hideHelp()) .option('-v, --verbose', 'Show detailed output.', count, 0) .option( '-d, --dictionary ', @@ -76,7 +77,7 @@ export function commandSuggestion(prog: Command): Command { options.useStdin = options.stdin; options.dictionaries = mergeArrays(options.dictionaries, options.dictionary); - if (!words.length && !options.useStdin) { + if (!words.length && !options.useStdin && !options.repl) { suggestionCommand.outputHelp(); throw new CheckFailed('outputHelp', 1); } diff --git a/packages/cspell/src/options.ts b/packages/cspell/src/options.ts index b398444dd97..3f9c5440263 100644 --- a/packages/cspell/src/options.ts +++ b/packages/cspell/src/options.ts @@ -110,6 +110,11 @@ export interface SuggestionOptions extends BaseOptions { * Use stdin for the input */ useStdin?: boolean; + + /** + * Use REPL interface for making suggestions. + */ + repl?: boolean; } export interface LegacyOptions { diff --git a/packages/cspell/src/repl/index.ts b/packages/cspell/src/repl/index.ts new file mode 100644 index 00000000000..64c2419b0a9 --- /dev/null +++ b/packages/cspell/src/repl/index.ts @@ -0,0 +1,59 @@ +import * as readline from 'readline'; + +export function simpleRepl(): AsyncIterable { + return new SimpleRepl(); +} + +export type CompleterResult = [string[], string]; +export type Completer = (line: string) => CompleterResult; + +export class SimpleRepl implements AsyncIterable { + public beforeEach: undefined | (() => void); + public completer: undefined | Completer; + private _history: string[]; + private rl: readline.ReadLine; + + constructor(public prompt = '> ') { + this._history = []; + this.rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + prompt, + history: this._history, + historySize: 100, + completer: (line: string) => this._completer(line), + }); + + this.rl.on('history', (h) => ((this._history = h), undefined)); + } + + question(query: string): Promise { + return new Promise((resolve) => { + this.rl.question(query, resolve); + }); + } + + private _completer(line: string): CompleterResult { + // console.log('Complete: %s', line); + // console.log('History: %o', this._history); + if (this.completer) return this.completer(line); + const hist = this._history.filter((h) => h.startsWith(line)); + return [hist, line]; + } + + get history() { + return this._history; + } + + [Symbol.asyncIterator]() { + const next = (): Promise> => { + if (this.beforeEach) this.beforeEach(); + // console.log('%o', this.rl); + return this.question(this.prompt) + .then((value: string) => ({ value })) + .catch(() => ({ done: true, value: undefined })); + }; + + return { next }; + } +} diff --git a/packages/cspell/tsconfig.json b/packages/cspell/tsconfig.json index 84be46e256f..5bf16e083f2 100644 --- a/packages/cspell/tsconfig.json +++ b/packages/cspell/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "skipLibCheck": true, "exactOptionalPropertyTypes": false, // make this true "outDir": "dist" },