1 | #!/usr/bin/env node
|
2 | "use strict";
|
3 | Object.defineProperty(exports, "__esModule", { value: true });
|
4 | const path = require("path");
|
5 | const program = require("commander");
|
6 | const npmPackage = require(path.join(__dirname, '..', 'package.json'));
|
7 | const application_1 = require("./application");
|
8 | const App = require("./application");
|
9 | const chalk = require("chalk");
|
10 |
|
11 | const templateIssue = `${chalk.green('${uri}')}:${chalk.yellow('${row}:${col}')} - Unknown word (${chalk.red('${text}')})`;
|
12 | const templateIssueLegacy = `${chalk.green('${uri}')}[\${row}, \${col}]: Unknown word: ${chalk.red('${text}')}`;
|
13 | const templateIssueWordsOnly = '${text}';
|
14 | function genIssueEmitter(template) {
|
15 | return function issueEmitter(issue) {
|
16 | console.log(formatIssue(template, issue));
|
17 | };
|
18 | }
|
19 | function errorEmitter(message, error) {
|
20 | console.error(chalk.red(message), error);
|
21 | return Promise.resolve();
|
22 | }
|
23 | function infoEmitter(message, msgType) {
|
24 | switch (msgType) {
|
25 | case 'Debug':
|
26 | console.info(chalk.cyan(message));
|
27 | break;
|
28 | case 'Info':
|
29 | console.info(chalk.yellow(message));
|
30 | break;
|
31 | case 'Progress':
|
32 | console.info(chalk.white(message));
|
33 | break;
|
34 | }
|
35 | }
|
36 | function debugEmitter(message) {
|
37 | infoEmitter(message, App.MessageTypes.Debug);
|
38 | }
|
39 | function nullEmitter(_) { }
|
40 | async function asyncNullEmitter(_) { }
|
41 | function getEmitters(options) {
|
42 | const issueTemplate = options.wordsOnly
|
43 | ? templateIssueWordsOnly
|
44 | : options.legacy ? templateIssueLegacy : templateIssue;
|
45 | const { silent = false, issues } = options;
|
46 | return {
|
47 | issue: silent ? nullEmitter : issues ? genIssueEmitter(issueTemplate) : nullEmitter,
|
48 | error: silent ? asyncNullEmitter : errorEmitter,
|
49 | info: silent ? nullEmitter : options.verbose ? infoEmitter : nullEmitter,
|
50 | debug: options.debug ? debugEmitter : nullEmitter,
|
51 | };
|
52 | }
|
53 | let showHelp = true;
|
54 | program
|
55 | .version(npmPackage.version)
|
56 | .description('Spelling Checker for Code')
|
57 | .option('-c, --config <cspell.json>', 'Configuration file to use. By default cspell looks for cspell.json in the current directory.')
|
58 | .option('--no-color', 'Turn off color.')
|
59 | .option('--color', 'Force color');
|
60 | program
|
61 | .option('-v, --verbose', 'display more information about the files being checked and the configuration')
|
62 | .option('--local <local>', 'Set language locals. i.e. "en,fr" for English and French, or "en-GB" for British English.')
|
63 | .option('--legacy', 'Legacy output')
|
64 | .option('--languageId <language>', 'Force programming language for unknown extensions. i.e. "php" or "scala"')
|
65 | .option('--wordsOnly', 'Only output the words not found in the dictionaries.')
|
66 | .option('-u, --unique', 'Only output the first instance of a word not found in the dictionaries.')
|
67 | .option('--debug', 'Output information useful for debugging cspell.json files.')
|
68 | .option('-e, --exclude <glob>', 'Exclude files matching the glob pattern')
|
69 | .option('--no-issues', 'Do not show the spelling errors.')
|
70 | .option('--no-summary', 'Turn off summary message in console')
|
71 | .option('-s, --silent', 'Silent mode, suppress error messages')
|
72 | .option('-r, --root <root folder>', 'Root directory, defaults to current directory.')
|
73 |
|
74 |
|
75 |
|
76 | .arguments('<files...>')
|
77 | .action((files, options) => {
|
78 | const emitters = getEmitters(options);
|
79 | if (!files || !files.length) {
|
80 | return;
|
81 | }
|
82 | showHelp = false;
|
83 | App.lint(files, options, emitters).then(result => {
|
84 | if (options.summary && !options.silent) {
|
85 | console.error('CSpell: Files checked: %d, Issues found: %d in %d files', result.files, result.issues, result.filesWithIssues.size);
|
86 | }
|
87 | process.exit(result.issues ? 1 : 0);
|
88 | }, (error) => {
|
89 | console.error(error.message);
|
90 | process.exit(1);
|
91 | });
|
92 | });
|
93 | program
|
94 | .command('trace')
|
95 | .description('Trace words')
|
96 | .option('-c, --config <cspell.json>', 'Configuration file to use. By default cspell looks for cspell.json in the current directory.')
|
97 | .option('--local <local>', 'Set language locals. i.e. "en,fr" for English and French, or "en-GB" for British English.')
|
98 | .option('--languageId <language>', 'Force programming language for unknown extensions. i.e. "php" or "scala"')
|
99 | .option('--no-color', 'Turn off color.')
|
100 | .option('--color', 'Force color')
|
101 | .arguments('<words...>')
|
102 | .action((words, options) => {
|
103 | showHelp = false;
|
104 | App.trace(words, options.parent).then(result => {
|
105 | result.forEach(emitTraceResult);
|
106 | process.exit(0);
|
107 | }, (error) => {
|
108 | console.error(error.message);
|
109 | process.exit(1);
|
110 | });
|
111 | });
|
112 | program
|
113 | .command('check <files...>')
|
114 | .description('Spell check file(s) and display the result. The full file is displayed in color.')
|
115 | .option('-c, --config <cspell.json>', 'Configuration file to use. By default cspell looks for cspell.json in the current directory.')
|
116 | .option('--no-color', 'Turn off color.')
|
117 | .option('--color', 'Force color')
|
118 | .action(async (files, options) => {
|
119 | showHelp = false;
|
120 | for (const filename of files) {
|
121 | console.log(chalk.yellowBright(`Check file: ${filename}`));
|
122 | console.log();
|
123 | try {
|
124 | const result = await application_1.checkText(filename, options.parent);
|
125 | for (const item of result.items) {
|
126 | const fn = item.flagIE === App.IncludeExcludeFlag.EXCLUDE
|
127 | ? chalk.gray
|
128 | : item.isError ? chalk.red : chalk.whiteBright;
|
129 | const t = fn(item.text);
|
130 | process.stdout.write(t);
|
131 | }
|
132 | console.log();
|
133 | }
|
134 | catch (e) {
|
135 | console.error(`Failed to read "${filename}"`);
|
136 | }
|
137 | console.log();
|
138 | }
|
139 | process.exit(0);
|
140 | });
|
141 |
|
142 |
|
143 |
|
144 |
|
145 |
|
146 |
|
147 |
|
148 |
|
149 |
|
150 |
|
151 |
|
152 |
|
153 |
|
154 |
|
155 |
|
156 | const usage = `
|
157 |
|
158 | Examples:
|
159 | cspell "*.js" Check all .js files in the current directory
|
160 | cspell "**/*.js" Check all .js files from the current directory
|
161 | cspell "src/**/*.js" Only check .js under src
|
162 | cspell "**/*.txt" "**/*.js" Check both .js and .txt files.
|
163 | cat LICENSE | cspell stdin Read from stdin the contents of LICENSE
|
164 | `;
|
165 | program.on('--help', function () {
|
166 | console.log(usage);
|
167 | });
|
168 | if (process.argv.length > 2) {
|
169 | program.parse(process.argv);
|
170 | }
|
171 | if (showHelp) {
|
172 | program.outputHelp();
|
173 | process.exit(1);
|
174 | }
|
175 | function emitTraceResult(r) {
|
176 | const terminalWidth = process.stdout.columns || 120;
|
177 | const widthName = 20;
|
178 | const w = chalk.green(r.word);
|
179 | const f = r.found
|
180 | ? chalk.whiteBright('*')
|
181 | : chalk.dim('-');
|
182 | const n = chalk.yellowBright(pad(r.dictName, widthName));
|
183 | const used = [r.word.length, 1, widthName].reduce((a, b) => a + b, 3);
|
184 | const widthSrc = terminalWidth - used;
|
185 | const s = chalk.white(trimMid(r.dictSource, widthSrc));
|
186 | const line = [w, f, n, s].join(' ');
|
187 | console.log(line);
|
188 | }
|
189 | function pad(s, w) {
|
190 | return (s + ' '.repeat(w)).substr(0, w);
|
191 | }
|
192 | function trimMid(s, w) {
|
193 | if (s.length <= w) {
|
194 | return s;
|
195 | }
|
196 | const l = Math.floor((w - 3) / 2);
|
197 | const r = Math.ceil((w - 3) / 2);
|
198 | return s.substr(0, l) + '...' + s.substr(-r);
|
199 | }
|
200 | function formatIssue(template, issue) {
|
201 | const { uri = '', row, col, text } = issue;
|
202 | return template
|
203 | .replace(/\$\{uri\}/, uri)
|
204 | .replace(/\$\{row\}/, row.toString())
|
205 | .replace(/\$\{col\}/, col.toString())
|
206 | .replace(/\$\{text\}/, text);
|
207 | }
|
208 |
|
\ | No newline at end of file |