* Formats a URI by converting it to a relative path if possible. * Handles URI decoding and gracefully falls back to un-decoded path if malformed. * Only uses relative paths when shorter and not starting with ../../
(uri: string | undefined, cwd?: string)
| 22 | * Only uses relative paths when shorter and not starting with ../../ |
| 23 | */ |
| 24 | function formatUri(uri: string | undefined, cwd?: string): string { |
| 25 | // Handle undefined/null URIs - this indicates malformed LSP data |
| 26 | if (!uri) { |
| 27 | // NOTE: This should ideally be caught earlier with proper error logging |
| 28 | // This is a defensive backstop in the formatting layer |
| 29 | logForDebugging( |
| 30 | 'formatUri called with undefined URI - indicates malformed LSP server response', |
| 31 | { level: 'warn' }, |
| 32 | ) |
| 33 | return '<unknown location>' |
| 34 | } |
| 35 | |
| 36 | // Remove file:// protocol if present |
| 37 | // On Windows, file:///C:/path becomes /C:/path after replacing file:// |
| 38 | // We need to strip the leading slash for Windows drive-letter paths |
| 39 | let filePath = uri.replace(/^file:\/\//, '') |
| 40 | if (/^\/[A-Za-z]:/.test(filePath)) { |
| 41 | filePath = filePath.slice(1) |
| 42 | } |
| 43 | |
| 44 | // Decode URI encoding - handle malformed URIs gracefully |
| 45 | try { |
| 46 | filePath = decodeURIComponent(filePath) |
| 47 | } catch (error) { |
| 48 | // Log for debugging but continue with un-decoded path |
| 49 | const errorMsg = errorMessage(error) |
| 50 | logForDebugging( |
| 51 | `Failed to decode LSP URI '${uri}': ${errorMsg}. Using un-decoded path: ${filePath}`, |
| 52 | { level: 'warn' }, |
| 53 | ) |
| 54 | // filePath already contains the un-decoded path, which is still usable |
| 55 | } |
| 56 | |
| 57 | // Convert to relative path if cwd is provided |
| 58 | if (cwd) { |
| 59 | // Normalize separators to forward slashes for consistent display output |
| 60 | const relativePath = relative(cwd, filePath).replaceAll('\\', '/') |
| 61 | // Only use relative path if it's shorter and doesn't start with ../.. |
| 62 | if ( |
| 63 | relativePath.length < filePath.length && |
| 64 | !relativePath.startsWith('../../') |
| 65 | ) { |
| 66 | return relativePath |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | // Normalize separators to forward slashes for consistent display output |
| 71 | return filePath.replaceAll('\\', '/') |
| 72 | } |
| 73 | |
| 74 | /** |
| 75 | * Groups items by their file URI. |
no test coverage detected