-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathformatting.ts
More file actions
202 lines (174 loc) · 7.58 KB
/
formatting.ts
File metadata and controls
202 lines (174 loc) · 7.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
import { spawn } from 'child_process'
import { readFileSync, writeFileSync, existsSync, mkdtempSync, unlinkSync } from 'fs'
import path from 'path'
import { tmpdir } from 'os'
import { fileURLToPath } from 'url'
// Function to handle code formatting with ruff
// Note: The bridge already converts relative URIs to absolute URIs before calling this function
export async function handleFormatting(msg: any, writer: any, wsWriter: any, ruffPath: string) {
try {
console.log('🎨 Formatting request intercepted, handling with ruff...')
const textDocument = msg.params?.textDocument
const uri = textDocument?.uri
if (!uri) {
console.error('❌ No URI in formatting request')
wsWriter.write({
jsonrpc: '2.0',
id: msg.id,
error: {
code: -32602,
message: 'Invalid params: missing textDocument.uri'
}
})
return
}
// Convert URI to file path (URI is already absolute from the bridge)
console.log(`📥 Received URI: ${uri}`)
let filePath: string
try {
filePath = fileURLToPath(uri)
console.log(`🔍 File path: ${filePath}`)
} catch (err) {
console.error(`❌ Failed to convert URI to path:`, err)
wsWriter.write({
jsonrpc: '2.0',
id: msg.id,
error: {
code: -32602,
message: 'Invalid file URI'
}
})
return
}
// Check if ruff exists
if (!existsSync(ruffPath)) {
console.error('❌ Ruff binary not found at:', ruffPath)
wsWriter.write({
jsonrpc: '2.0',
id: msg.id,
error: {
code: -32000,
message: 'Ruff binary not available'
}
})
return
}
// For full document formatting, we need to format the file on disk
// In a real implementation, you might want to handle range formatting differently
const isRangeFormatting = msg.method === 'textDocument/rangeFormatting'
const isFullFormatting = msg.method === 'textDocument/formatting'
if (isRangeFormatting) {
// For range formatting, we need to read the file, apply changes to the range,
// write to temp file, format, then extract the formatted range
console.log('📝 Range formatting not fully implemented yet, falling back to full document formatting')
}
if (!isFullFormatting && !isRangeFormatting) {
console.log('⚠️ Unsupported formatting method:', msg.method)
// Let Pyright handle other formatting methods
writer.write(msg)
return
}
// Create a temporary file for formatting
const tempDir = mkdtempSync(path.join(tmpdir(), 'ruff-format-'))
const tempFile = path.join(tempDir, 'temp.py')
try {
// Read the current file content
// Note: For formatting, we should use the file on disk as the source of truth
let fileContent: string
if (existsSync(filePath)) {
fileContent = readFileSync(filePath, 'utf-8')
console.log(`📖 Read file from disk: ${filePath}`)
console.log(` Content length: ${fileContent.length} chars`)
} else {
// If file doesn't exist, use empty content or content from params
fileContent = msg.params?.textDocument?.text || ''
console.log(`⚠️ File not found on disk, using params or empty content`)
}
// Write content to temp file
writeFileSync(tempFile, fileContent)
console.log(`💾 Wrote content to temp file: ${tempFile}`)
// Run ruff format on the temp file
const ruff = spawn(ruffPath, ['format', tempFile], {
cwd: tempDir,
stdio: ['pipe', 'pipe', 'pipe']
})
let stdout = ''
let stderr = ''
ruff.stdout.on('data', (data) => {
stdout += data.toString()
})
ruff.stderr.on('data', (data) => {
stderr += data.toString()
})
await new Promise((resolve, reject) => {
ruff.on('close', (code) => {
console.log(`🔧 Ruff process exited with code: ${code}`)
if (stderr) console.log(` stderr: ${stderr}`)
if (stdout) console.log(` stdout: ${stdout}`)
if (code === 0) {
resolve(void 0)
} else {
reject(new Error(`Ruff exited with code ${code}: ${stderr}`))
}
})
ruff.on('error', reject)
})
// Read the formatted content
const formattedContent = readFileSync(tempFile, 'utf-8')
// Calculate the text edits - replace entire document
const lines = fileContent.split('\n')
const lineCount = lines.length
const hasTrailingNewline = lines[lineCount - 1] === ''
// Calculate the correct end position for full document replacement
let endLine: number
let endCharacter: number
if (hasTrailingNewline && lineCount >= 2) {
// Document ends with newline, end at the end of the last content line
endLine = lineCount - 2
endCharacter = lines[lineCount - 2].length
} else {
// Document doesn't end with newline, end at the end of the last line
endLine = lineCount - 1
endCharacter = lines[lineCount - 1].length
}
const edits = [{
range: {
start: { line: 0, character: 0 },
end: { line: endLine, character: endCharacter }
},
newText: formattedContent
}]
console.log('✅ Formatting completed successfully')
console.log(`📤 Sending response with ${edits.length} edit(s)`)
console.log(` Range: (${edits[0].range.start.line},${edits[0].range.start.character}) -> (${edits[0].range.end.line},${edits[0].range.end.character})`)
console.log(` New text length: ${formattedContent.length} chars`)
console.log(` First 100 chars: ${formattedContent.substring(0, 100)}`)
// Send the response
const response = {
jsonrpc: '2.0',
id: msg.id,
result: edits
}
console.log('📨 Response object:', JSON.stringify(response).substring(0, 500))
wsWriter.write(response)
} finally {
// Clean up temp files
try {
if (existsSync(tempFile)) unlinkSync(tempFile)
// Note: tempDir cleanup would be handled by OS, but in production you might want to clean it up
} catch (err) {
console.warn('⚠️ Failed to clean up temp file:', err)
}
}
} catch (error) {
console.error('❌ Formatting error:', error)
wsWriter.write({
jsonrpc: '2.0',
id: msg.id,
error: {
code: -32000,
message: `Formatting failed: ${error.message}`
}
})
}
}