Skip to content

Commit d9ed169

Browse files
authored
fix: cspell-rpc - reduce the size of an RPC result. (#8574)
1 parent bb02314 commit d9ed169

10 files changed

Lines changed: 220 additions & 43 deletions

File tree

packages/cspell-io/src/node/file/fetch.test.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,15 @@ describe('fetch', () => {
2828
vi.resetAllMocks();
2929
});
3030

31-
test(
32-
'fetchURL',
33-
async () => {
34-
const url = new URL('https://example.com/');
35-
const response = await doFetchUrl(url);
36-
expect(response).toBeInstanceOf(Uint8Array);
37-
},
38-
timeout,
39-
);
31+
// test.only(
32+
// 'fetchURL',
33+
// async () => {
34+
// const url = new URL('https://example.com/');
35+
// const response = await doFetchUrl(url);
36+
// expect(response).toBeInstanceOf(Uint8Array);
37+
// },
38+
// timeout,
39+
// );
4040

4141
/*
4242
test.each`

packages/cspell-lib/api/api.d.ts

Lines changed: 52 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/cspell-lib/src/cspell-rpc/index.test.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,31 @@ describe('Validate Client / Server communications', () => {
3232
const doc = { uri: import.meta.url };
3333
const result = await api.spellCheckDocument(doc, {}, {});
3434
expect(result).toBeDefined();
35-
expect(result).toEqual(oc({ issues: [], errors: undefined }));
36-
37-
const result2 = await api.spellCheckDocument(doc, {}, {});
35+
expect(result).toEqual({
36+
issues: undefined,
37+
checked: true,
38+
document: doc,
39+
perf: undefined,
40+
});
41+
42+
const result2 = await api.spellCheckDocument(doc, { measurePerf: true }, {});
3843
expect(result2).toBeDefined();
39-
expect(result2).toEqual(oc({ issues: [], errors: undefined }));
44+
expect(result2).toEqual({
45+
issues: undefined,
46+
checked: true,
47+
document: doc,
48+
perf: expect.anything(),
49+
});
4050
});
4151

4252
const urlFixtures = new URL('fixtures/', packageUrl);
4353
const urlSampleFilesWithIssues = new URL('docValidator/sample-files-with-issues/', urlFixtures);
44-
const noIssues = { issues: [], errors: undefined, configErrors: undefined, dictionaryErrors: undefined };
54+
const noIssuesRPC = { issues: undefined, errors: undefined, configErrors: undefined, dictionaryErrors: undefined };
4555

4656
const expectedFor = {
47-
'WOX_Permissions.ps1': { ...noIssues, issues: ac([oc({ text: 'explicitily' })]) }, // cspell:ignore explicitily
57+
'WOX_Permissions.ps1': { ...noIssuesRPC, issues: ac([oc({ text: 'explicitily' })]) }, // cspell:ignore explicitily
4858
'import-errors/file.txt': {
49-
...noIssues,
59+
...noIssuesRPC,
5060
configErrors: [
5161
{
5262
filename: expect.stringContaining('missing.config.yaml'),
@@ -68,7 +78,7 @@ describe('Validate Client / Server communications', () => {
6878

6979
test.each`
7080
filename | rootUrl | expected
71-
${import.meta.url} | ${import.meta.url} | ${noIssues}
81+
${import.meta.url} | ${import.meta.url} | ${noIssuesRPC}
7282
${'WOX_Permissions.ps1'} | ${urlSampleFilesWithIssues} | ${expectedFor['WOX_Permissions.ps1']}
7383
${'import-errors/file.txt'} | ${urlFixtures} | ${expectedFor['import-errors/file.txt']}
7484
`('spell document $filename', async ({ filename, rootUrl, expected }) => {
Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,42 @@
11
import type { ExtendedSuggestion } from './Suggestion.js';
2-
import type { ValidationResult } from './ValidationResult.js';
2+
import type { ValidationResult, ValidationResultRPC } from './ValidationResult.js';
33

44
export interface ValidationIssue extends ValidationResult {
55
suggestions?: string[] | undefined;
66
suggestionsEx?: ExtendedSuggestion[] | undefined;
77
}
8+
9+
/**
10+
* The ValidationIssueRPC is used for RPC communication. It is a subset of ValidationIssue that can be serialized.
11+
*/
12+
export interface ValidationIssueRPC extends ValidationResultRPC {
13+
suggestionsEx?: ExtendedSuggestion[] | undefined;
14+
}
15+
16+
export function toValidationIssueRPC(issue: ValidationIssue, index?: number): ValidationIssueRPC;
17+
export function toValidationIssueRPC(issue: ValidationIssue): ValidationIssueRPC {
18+
const {
19+
text,
20+
length,
21+
offset,
22+
message,
23+
issueType,
24+
hasPreferredSuggestions,
25+
hasSimpleSuggestions,
26+
isFlagged,
27+
isFound,
28+
suggestionsEx,
29+
} = issue;
30+
return {
31+
text,
32+
offset,
33+
length,
34+
message,
35+
issueType,
36+
hasPreferredSuggestions,
37+
hasSimpleSuggestions,
38+
isFlagged,
39+
isFound,
40+
suggestionsEx,
41+
};
42+
}
Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,43 @@
1-
import type { Issue, TextOffset as TextOffsetRW } from '@cspell/cspell-types';
1+
import type { Issue, TextOffset as TextOffset } from '@cspell/cspell-types';
22

33
export interface ValidationResult
4-
extends TextOffsetRW, Pick<Issue, 'message' | 'issueType' | 'hasPreferredSuggestions' | 'hasSimpleSuggestions'> {
5-
line: TextOffsetRW;
4+
extends
5+
Omit<TextOffset, 'length'>,
6+
Pick<Issue, 'message' | 'issueType' | 'hasPreferredSuggestions' | 'hasSimpleSuggestions'> {
7+
length?: number | undefined;
8+
line: TextOffset;
69
isFlagged?: boolean | undefined;
710
isFound?: boolean | undefined;
811
}
12+
13+
/**
14+
* The ValidationResultRPC is used for RPC communication. It is a subset of ValidationResult that can be serialized.
15+
*
16+
* The URI, document, row, and column information are not included in the RPC version of ValidationResult
17+
* because they can be calculated from the offset and the document text.
18+
*/
19+
export interface ValidationResultRPC extends Pick<
20+
ValidationResult,
21+
| 'text'
22+
| 'length'
23+
| 'offset'
24+
| 'message'
25+
| 'issueType'
26+
| 'hasPreferredSuggestions'
27+
| 'hasSimpleSuggestions'
28+
| 'isFlagged'
29+
| 'isFound'
30+
> {
31+
/**
32+
* The line information is not included in the RPC version of ValidationResult because it can be calculated from the offset and the document text.
33+
*/
34+
line?: undefined;
35+
/**
36+
* The context information is not included in the RPC version of ValidationResult because it can be calculated from the offset and the document text.
37+
*/
38+
context?: undefined;
39+
uri?: undefined;
40+
doc?: undefined;
41+
row?: undefined;
42+
col?: undefined;
43+
}

packages/cspell-lib/src/lib/spellCheckFile.ts

Lines changed: 59 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,14 @@ import type { Document, DocumentWithText } from './Document/index.js';
66
import { isBinaryDoc } from './Document/isBinaryDoc.js';
77
import { documentToTextDocument, resolveDocument } from './Document/resolveDocument.js';
88
import { createTextDocument } from './Models/TextDocument.js';
9+
import { toValidationIssueRPC, type ValidationIssueRPC } from './Models/ValidationIssue.js';
910
import { createPerfTimer } from './perf/index.js';
1011
import type { ImportFileRefWithError } from './Settings/index.js';
11-
import { cloneSettingsForExport } from './Settings/sanitizeSettings.js';
1212
import { determineTextDocumentSettings } from './textValidation/determineTextDocumentSettings.js';
1313
import type { DocumentValidatorOptions } from './textValidation/index.js';
1414
import { DocumentValidator } from './textValidation/index.js';
1515
import { isError } from './util/errors.js';
1616
import type { Uri } from './util/IUri.js';
17-
import { memoizeLastCall } from './util/memoizeLastCall.js';
1817
import { toUri } from './util/Uri.js';
1918
import type { ValidateTextOptions, ValidationIssue } from './validator.js';
2019

@@ -40,6 +39,10 @@ export interface SpellCheckFileOptions extends ValidateTextOptions, Pick<CSpellU
4039
noConfigSearch?: boolean;
4140
}
4241

42+
export interface SpellCheckFileOptionsRPC extends SpellCheckFileOptions {
43+
measurePerf?: boolean;
44+
}
45+
4346
export interface SpellCheckFilePerf extends Record<string, number | undefined> {
4447
loadTimeMs?: number;
4548
prepareTimeMs?: number;
@@ -60,6 +63,28 @@ export interface SpellCheckFileResult {
6063
perf?: SpellCheckFilePerf;
6164
}
6265

66+
interface DocumentReferenceRPC extends Pick<Document, 'uri'> {
67+
text?: undefined;
68+
languageId?: undefined;
69+
locale?: undefined;
70+
}
71+
72+
export interface SpellCheckFileResultRPC {
73+
/**
74+
* The document that was checked.
75+
*
76+
* **Note:** the text will be missing to avoid sending large amounts of text over the RPC channel.
77+
* If the text is needed, the document should be reloaded using the URI.
78+
*/
79+
document: DocumentReferenceRPC;
80+
issues?: ValidationIssueRPC[] | undefined;
81+
checked: boolean;
82+
errors?: Error[] | undefined;
83+
configErrors?: ImportFileRefWithError[] | undefined;
84+
dictionaryErrors?: Map<string, Error[]> | undefined;
85+
perf?: SpellCheckFilePerf | undefined;
86+
}
87+
6388
/**
6489
* Spell Check a file
6590
* @param file - absolute path to file to read and check.
@@ -135,12 +160,6 @@ export async function spellCheckDocument(
135160
}
136161
}
137162

138-
const memoizedCloneSettingsForExport = memoizeLastCall(cloneSettingsForExport);
139-
140-
function sanitizeSettingsForExport(settings: CSpellSettingsWithSourceTrace | undefined): CSpellSettingsWithSourceTrace {
141-
return settings ? memoizedCloneSettingsForExport(settings) : {};
142-
}
143-
144163
/**
145164
* Spell Check a Document.
146165
* @param document - document to be checked. If `document.text` is `undefined` the file will be loaded
@@ -149,11 +168,39 @@ function sanitizeSettingsForExport(settings: CSpellSettingsWithSourceTrace | und
149168
*/
150169
export async function spellCheckDocumentRPC(
151170
document: Document | DocumentWithText,
152-
options: SpellCheckFileOptions,
171+
options: SpellCheckFileOptionsRPC,
153172
settingsOrConfigFile: CSpellUserSettings | ICSpellConfigFile,
154-
): Promise<SpellCheckFileResult> {
155-
const result = { ...(await spellCheckDocument(document, options, settingsOrConfigFile)) };
156-
result.settingsUsed = sanitizeSettingsForExport(result.settingsUsed);
173+
): Promise<SpellCheckFileResultRPC> {
174+
const { issues, checked, errors, configErrors, dictionaryErrors, perf } = await spellCheckDocument(
175+
document,
176+
options,
177+
settingsOrConfigFile,
178+
);
179+
180+
const result: SpellCheckFileResultRPC = {
181+
document: { uri: document.uri },
182+
checked,
183+
};
184+
185+
if (issues.length) {
186+
result.issues = issues.map(toValidationIssueRPC);
187+
}
188+
189+
if (errors?.length) {
190+
result.errors = errors;
191+
}
192+
193+
if (configErrors?.length) {
194+
result.configErrors = configErrors;
195+
}
196+
if (dictionaryErrors?.size) {
197+
result.dictionaryErrors = dictionaryErrors;
198+
}
199+
200+
if (perf && options.measurePerf) {
201+
result.perf = perf;
202+
}
203+
157204
return result;
158205
}
159206

packages/cspell-types/src/TextOffset.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ export interface TextOffset {
1111
/**
1212
* Assumed to match `text.length` if the text has not been transformed.
1313
*/
14-
length?: number;
14+
length?: number | undefined;
1515
}
1616

1717
export interface TextDocumentOffset extends TextOffset {
18-
uri?: string;
18+
uri?: string | undefined;
1919
doc: string;
2020
row: number;
2121
col: number;

0 commit comments

Comments
 (0)