Skip to content

Commit

Permalink
feat: Support local configuration files (#966)
Browse files Browse the repository at this point in the history
## Supported Configuration Files

The spell checker will look for the following configuration files.

- `.cspell.json`
- `cspell.json`
- `.cSpell.json`
- `cSpell.json`
- `.vscode/cspell.json`
- `.vscode/cSpell.json`
- `.vscode/.cspell.json`
- `cspell.config.js`
- `cspell.config.cjs`
- `cspell.config.json`
- `cspell.config.yaml`
- `cspell.config.yml`
- `cspell.yaml`
- `cspell.yml`

## Configuration Search

While spell checking files, the spell checker will look for the nearest configuration file in the directory hierarchy.
This allows for folder level configurations to be honored.
It is possible to stop this behavior by adding adding `"noConfigSearch": true` to the top level configuration.

A Monorepo Example:

```
repo-root
├── cspell.config.json
├─┬ packages
│ ├─┬ package-A
│ │ ├── cspell.json
│ │ ├── README.md
│ │ └── CHANGELOG.md
│ ├─┬ package-B
│ │ ├── README.md
│ │ └── CHANGELOG.md
│ ├─┬ package-C
│ │ ├── cspell.yaml
│ │ ├── README.md
│ │ └── CHANGELOG.md
```

The following command will search the repo start at `repo-root` looking for `.md` files.

```
repo-root % cspell "**/*.md"
```

The root configuration is used to determine which files to check. Files matching the globs in `ignorePaths` will not be checked. When a file is found, the directory hierarchy is searched looking for the nearest configuration file.

For example:

| File                           | Config Used                      |
| ------------------------------ | -------------------------------- |
| `packages/package-A/README.md` | `packages/package-A/cspell.json` |
| `packages/package-A/CONFIG.md` | `packages/package-A/cspell.json` |
| `packages/package-B/README.md` | `cspell.config.json`             |
| `packages/package-C/README.md` | `packages/package-C/cspell.yaml` |
* dev: Add `noConfigSearch` to configuration

Support reading local config files when spell checking a file.

This makes having supporting workspaces with multiple folders easier.

## Commits
* dev: improve sample config
* dev: document how the configuration works.
* dev: Support spell checking documents
* dev: Update spellCheckFile.test.ts to fix tests on Windows
* dev: add ability to specify the languageId in the Document
* dev: refactor Document type.
* dev: export spellCheckDocument
* refactor: cspell app options into their own files.
  • Loading branch information
Jason3S authored Feb 18, 2021
1 parent 8fdf3e9 commit 0ccc5fe
Show file tree
Hide file tree
Showing 18 changed files with 611 additions and 148 deletions.
5 changes: 5 additions & 0 deletions cspell.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,11 @@
"description": "Optional name of configuration",
"type": "string"
},
"noConfigSearch": {
"default": false,
"description": "Prevents searching for local configuration when checking individual documents.",
"type": "boolean"
},
"numSuggestions": {
"default": 10,
"description": "Number of suggestions to make",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
{
"version": "0.1",
"id": "cspell-project-config",
"name": "cspell Project Config",
"version": "0.2",
"id": "cspell-package-config",
"name": "cspell Package Config",
"language": "en",
"words": [
"gensequence",
"xregexp"
],
"words": ["gensequence"],
"maxNumberOfProblems": 100,
"ignorePaths": [
"dictionaries/**",
Expand All @@ -23,7 +20,5 @@
"allowCompoundWords": false,
"dictionaryDefinitions": [],
"ignoreWords": [],
"import": [
"../../cspell.json"
]
"import": ["../../cspell.json"]
}
108 changes: 108 additions & 0 deletions packages/cspell-lib/docs/configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# cspell Configuration

## Supported Configuration Files

The spell checker will look for the following configuration files.

- `.cspell.json`
- `cspell.json`
- `.cSpell.json`
- `cSpell.json`
- `.vscode/cspell.json`
- `.vscode/cSpell.json`
- `.vscode/.cspell.json`
- `cspell.config.js`
- `cspell.config.cjs`
- `cspell.config.json`
- `cspell.config.yaml`
- `cspell.config.yml`
- `cspell.yaml`
- `cspell.yml`

## Configuration Search

While spell checking files, the spell checker will look for the nearest configuration file in the directory hierarchy.
This allows for folder level configurations to be honored.
It is possible to stop this behavior by adding adding `"noConfigSearch": true` to the top level configuration.

A Monorepo Example:

```
repo-root
├── cspell.config.json
├─┬ packages
│ ├─┬ package-A
│ │ ├── cspell.json
│ │ ├── README.md
│ │ └── CHANGELOG.md
│ ├─┬ package-B
│ │ ├── README.md
│ │ └── CHANGELOG.md
│ ├─┬ package-C
│ │ ├── cspell.yaml
│ │ ├── README.md
│ │ └── CHANGELOG.md
```

The following command will search the repo start at `repo-root` looking for `.md` files.

```
repo-root % cspell "**/*.md"
```

The root configuration is used to determine which files to check. Files matching the globs in `ignorePaths` will not be checked. When a file is found, the directory hierarchy is searched looking for the nearest configuration file.

For example:

| File | Config Used |
| ------------------------------ | -------------------------------- |
| `packages/package-A/README.md` | `packages/package-A/cspell.json` |
| `packages/package-A/CONFIG.md` | `packages/package-A/cspell.json` |
| `packages/package-B/README.md` | `cspell.config.json` |
| `packages/package-C/README.md` | `packages/package-C/cspell.yaml` |

## Example Configurations:

### Example `cspell.config.js`

```javascript
'use strict';

/** @type { import("@cspell/cspell-types").CSpellUserSettings } */
const cspell = {
description: 'Company cspell settings',
languageSettings: [
{
languageId: 'cpp',
allowCompoundWords: false,
patterns: [
{
// define a pattern to ignore #includes
name: 'pound-includes',
pattern: /^\s*#include.*/g,
},
],
ignoreRegExpList: ['pound-includes'],
},
],
dictionaryDefinitions: [
{
name: 'custom-words',
path: './custom-words.txt',
},
],
dictionaries: ['custom-words'],
};

module.exports = cspell;
```

### Example import from `cspell.json`

Import a `cspell.config.js` file from a `cspell.json` file.

```javascript
{
"import": ["../cspell.config.js"]
}
```
20 changes: 20 additions & 0 deletions packages/cspell-lib/samples/js-config/cspell.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,26 @@
/** @type { import("@cspell/cspell-types").CSpellUserSettings } */
const cspell = {
description: 'cspell.config.js file in samples/js-config',
languageSettings: [
{
languageId: 'cpp',
allowCompoundWords: false,
patterns: [
{
name: 'pound-includes',
pattern: /^\s*#include.*/g,
},
],
ignoreRegExpList: ['pound-includes'],
},
],
dictionaryDefinitions: [
{
name: 'custom-words',
path: './custom-words.txt',
},
],
dictionaries: ['custom-words'],
};

module.exports = cspell;
4 changes: 4 additions & 0 deletions packages/cspell-lib/samples/js-config/custom-words.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
here
are
some
words
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ describe('Validate CSpellSettingsServer', () => {
});

test('tests loading a missing cSpell.json file', () => {
const filename = path.join(__dirname, '..', '..', 'cSpell.json');
const filename = path.join(__dirname, '..', '..', 'cspell.config.json');
const settings = readSettings(filename);
expect(settings.__importRef?.filename).toBe(path.resolve(filename));
expect(settings.__importRef?.error).toBeUndefined();
Expand Down
7 changes: 1 addition & 6 deletions packages/cspell-lib/src/Settings/CSpellSettingsServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { resolveFile } from '../util/resolveFile';
import { getRawGlobalSettings } from './GlobalSettings';
import { cosmiconfig, cosmiconfigSync, OptionsSync as CosmicOptionsSync, Options as CosmicOptions } from 'cosmiconfig';
import { GlobMatcher } from 'cspell-glob';
import { ImportError } from './ImportError';

const currentSettingsFileVersion = '0.1';

Expand Down Expand Up @@ -543,12 +544,6 @@ export function extractImportErrors(settings: CSpellSettings): ImportFileRefWith
return !imports ? [] : [...imports.values()].filter(isImportFileRefWithError);
}

class ImportError extends Error {
constructor(msg: string, readonly cause?: Error) {
super(msg);
}
}

function resolveGlobRoot(settings: CSpellSettings, pathToSettingsFile: string): string {
const envGlobRoot = process.env[ENV_CSPELL_GLOB_ROOT];
const defaultGlobRoot = envGlobRoot ?? '${cwd}';
Expand Down
5 changes: 5 additions & 0 deletions packages/cspell-lib/src/Settings/ImportError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export class ImportError extends Error {
constructor(msg: string, readonly cause?: Error) {
super(msg);
}
}
13 changes: 10 additions & 3 deletions packages/cspell-lib/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,30 @@ export { TextOffset, TextDocumentOffset } from './util/text';
export {
checkText,
CheckTextInfo,
TextInfoItem,
IncludeExcludeOptions,
IncludeExcludeFlag,
IncludeExcludeOptions,
TextInfoItem,
validateText,
} from './validator';
export { defaultFileName as defaultSettingsFilename } from './Settings';
export {
CompoundWordsMethod,
createSpellingDictionary,
getDictionary,
refreshDictionaryCache,
SpellingDictionary,
SuggestionCollector,
SuggestionResult,
refreshDictionaryCache,
} from './SpellingDictionary';
export { combineTextAndLanguageSettings } from './Settings/TextDocumentSettings';
export { combineTextAndLanguageSettings as constructSettingsForText } from './Settings/TextDocumentSettings';
export {
Document,
spellCheckDocument,
spellCheckFile,
SpellCheckFileOptions,
SpellCheckFileResult,
} from './spellCheckFile';

import * as Text from './util/text';
import * as Link from './Settings/link';
Expand Down
Loading

0 comments on commit 0ccc5fe

Please sign in to comment.