Skip to content

Commit a416fd0

Browse files
committed
✨ feat: improve error handling and messaging in PhpmdService
1 parent 02d6ea2 commit a416fd0

File tree

2 files changed

+176
-5
lines changed

2 files changed

+176
-5
lines changed

src/services/phpmd-service.ts

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,17 @@ export class PhpmdService {
139139

140140
// Check if it's a DDEV project
141141
if (!DdevUtils.isDdevProject(document)) {
142-
vscode.window.showErrorMessage('This workspace is not a DDEV project. Please make sure: 1. You are in a DDEV project directory, 2. The .ddev/config.yaml file exists, 3. DDEV is running (use "ddev start")');
142+
vscode.window.showErrorMessage(
143+
'DDEV environment not detected',
144+
{
145+
modal: false,
146+
detail: 'This extension requires a running DDEV environment. Please make sure:\n\n' +
147+
'1. You are in a DDEV project directory\n' +
148+
'2. The .ddev/config.yaml file exists\n' +
149+
'3. DDEV is running (use "ddev start" in terminal)\n\n' +
150+
'If DDEV is already running, try restarting it with "ddev restart".'
151+
}
152+
);
143153
return;
144154
}
145155

@@ -161,9 +171,26 @@ export class PhpmdService {
161171

162172
// Process output
163173
this.processPhpmdOutput(output, document);
164-
} catch (error) {
174+
} catch (error: any) {
165175
console.error('Error running PHPMD:', error);
166-
vscode.window.showErrorMessage('Error running PHPMD. Make sure PHPMD is installed in your DDEV container.');
176+
177+
// Show a more detailed error message to the user
178+
let errorMessage = error.message || String(error);
179+
180+
// Cut the message at first occurrence of \n\n
181+
const doubleCrlfPosition = errorMessage.indexOf('\n\n');
182+
if (doubleCrlfPosition !== -1) {
183+
errorMessage = errorMessage.substring(0, doubleCrlfPosition);
184+
}
185+
186+
const detailedMessage = error.stderr
187+
? `PHPMD Error: ${error.stderr.split('\n\n')[0]}`
188+
: `Error running PHPMD: ${errorMessage}`;
189+
190+
vscode.window.showErrorMessage(
191+
detailedMessage,
192+
{ modal: false, detail: 'Make sure PHPMD is installed in your DDEV container.' }
193+
);
167194
}
168195
}
169196

@@ -192,6 +219,9 @@ export class PhpmdService {
192219
* @param document Document that was analyzed
193220
*/
194221
private processPhpmdOutput(output: string, document: vscode.TextDocument): void {
222+
// Store original output for error reporting
223+
const originalOutput = output;
224+
195225
try {
196226
// Parse output
197227
const result = JSON.parse(output) as PhpmdResult;
@@ -215,9 +245,32 @@ export class PhpmdService {
215245
if (diagnostics.length > 0) {
216246
this.diagnosticCollection.set(document.uri, diagnostics);
217247
}
218-
} catch (error) {
248+
} catch (error: any) {
219249
console.error('Error processing PHPMD output:', error);
220-
vscode.window.showErrorMessage(`Error processing PHPMD output: ${error}`);
250+
251+
// Extract a readable error message
252+
let errorMessage = error.message || String(error);
253+
254+
// Cut the message at first occurrence of \n\n
255+
const doubleCrlfPosition = errorMessage.indexOf('\n\n');
256+
if (doubleCrlfPosition !== -1) {
257+
errorMessage = errorMessage.substring(0, doubleCrlfPosition);
258+
}
259+
260+
// Show the raw output for debugging if it's available
261+
let detail = 'There was a problem processing the PHPMD output.';
262+
if (originalOutput) {
263+
const shortenedOutput = originalOutput.indexOf('\n\n') !== -1
264+
? originalOutput.substring(0, originalOutput.indexOf('\n\n'))
265+
: originalOutput.substring(0, 200);
266+
267+
detail += `\n\nRaw output:\n${shortenedOutput}${originalOutput.length > shortenedOutput.length ? '...' : ''}`;
268+
}
269+
270+
vscode.window.showErrorMessage(
271+
`Error processing PHPMD output: ${errorMessage}`,
272+
{ modal: false, detail: detail }
273+
);
221274
}
222275
}
223276

src/test/extension.test.ts

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,4 +327,122 @@ suite('Extension Test Suite', () => {
327327
assert.fail('Second diagnostic should also have code object with target');
328328
}
329329
});
330+
331+
// Test for error message truncation at \n\n
332+
test('Error messages are truncated at first \\n\\n occurrence', async () => {
333+
// Test error message truncation
334+
const truncateErrorMessage = (message: string): string => {
335+
const doubleCrlfPosition = message.indexOf('\n\n');
336+
if (doubleCrlfPosition !== -1) {
337+
return message.substring(0, doubleCrlfPosition);
338+
}
339+
return message;
340+
};
341+
342+
// Sample error messages
343+
const singleLineError = 'Error: PHPMD command failed';
344+
const multiLineError = 'Error: PHPMD command failed\nSome additional info';
345+
const errorWithDoubleNewline = 'Error: PHPMD command failed\n\nStack trace:\nat line 1\nat line 2';
346+
const multipleDoubleNewlines = 'Error: PHPMD command failed\n\nStack trace\n\nMore details';
347+
348+
// Test truncation
349+
assert.strictEqual(
350+
truncateErrorMessage(singleLineError),
351+
singleLineError,
352+
'Single line error should remain unchanged'
353+
);
354+
355+
assert.strictEqual(
356+
truncateErrorMessage(multiLineError),
357+
multiLineError,
358+
'Error with single newlines should remain unchanged'
359+
);
360+
361+
assert.strictEqual(
362+
truncateErrorMessage(errorWithDoubleNewline),
363+
'Error: PHPMD command failed',
364+
'Error with double newline should be truncated at first occurrence'
365+
);
366+
367+
assert.strictEqual(
368+
truncateErrorMessage(multipleDoubleNewlines),
369+
'Error: PHPMD command failed',
370+
'Error with multiple double newlines should be truncated at first occurrence'
371+
);
372+
});
373+
374+
// Test for stderr message truncation
375+
test('STDERR messages are truncated at first \\n\\n occurrence', async () => {
376+
// Test stderr truncation similar to what we're doing in the error handler
377+
const truncateStderr = (stderr: string): string => {
378+
return stderr.split('\n\n')[0];
379+
};
380+
381+
// Sample stderr outputs
382+
const singleLineStderr = 'Command failed with exit code 1';
383+
const multiLineStderr = 'Command failed with exit code 1\nAdditional error details';
384+
const stderrWithDoubleNewline = 'Command failed with exit code 1\n\nDetailed error information';
385+
386+
// Test truncation
387+
assert.strictEqual(
388+
truncateStderr(singleLineStderr),
389+
singleLineStderr,
390+
'Single line stderr should remain unchanged'
391+
);
392+
393+
assert.strictEqual(
394+
truncateStderr(multiLineStderr),
395+
multiLineStderr,
396+
'Stderr with single newlines should remain unchanged'
397+
);
398+
399+
assert.strictEqual(
400+
truncateStderr(stderrWithDoubleNewline),
401+
'Command failed with exit code 1',
402+
'Stderr with double newline should be truncated at first occurrence'
403+
);
404+
});
405+
406+
// Test for raw output truncation
407+
test('Raw output is truncated at first \\n\\n or limited to 200 characters', async () => {
408+
// Test raw output truncation similar to what we do in processPhpmdOutput error handling
409+
const truncateRawOutput = (output: string): string => {
410+
const doubleCrlfPosition = output.indexOf('\n\n');
411+
if (doubleCrlfPosition !== -1) {
412+
return output.substring(0, doubleCrlfPosition);
413+
}
414+
return output.substring(0, Math.min(output.length, 200));
415+
};
416+
417+
// Sample outputs
418+
const shortOutput = 'Valid JSON output';
419+
const longOutputNoNewlines = 'x'.repeat(300); // 300 characters with no newlines
420+
const outputWithNewlines = 'First part of output\n\nSecond part that should be truncated';
421+
const outputWithMultipleDoubleNewlines = 'Start\n\nMiddle\n\nEnd';
422+
423+
// Test truncation
424+
assert.strictEqual(
425+
truncateRawOutput(shortOutput),
426+
shortOutput,
427+
'Short output should remain unchanged'
428+
);
429+
430+
assert.strictEqual(
431+
truncateRawOutput(longOutputNoNewlines).length,
432+
200,
433+
'Long output without newlines should be truncated to 200 characters'
434+
);
435+
436+
assert.strictEqual(
437+
truncateRawOutput(outputWithNewlines),
438+
'First part of output',
439+
'Output with newlines should be truncated at first \\n\\n'
440+
);
441+
442+
assert.strictEqual(
443+
truncateRawOutput(outputWithMultipleDoubleNewlines),
444+
'Start',
445+
'Output with multiple double newlines should be truncated at first occurrence'
446+
);
447+
});
330448
});

0 commit comments

Comments
 (0)