Skip to content

Commit c205b01

Browse files
committed
fix(amazonq) Previous and subsequent cells are used as context for completion in a Jupyter notebook
1 parent 4451211 commit c205b01

File tree

2 files changed

+79
-94
lines changed

2 files changed

+79
-94
lines changed

packages/amazonq/test/unit/codewhisperer/util/editorContext.test.ts

Lines changed: 56 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -214,127 +214,122 @@ describe('editorContext', function () {
214214
})
215215

216216
describe('extractPrefixCellsContext', function () {
217-
it('Should extract content from cells in reverse order up to maxLength', function () {
217+
it('Should extract content from cells in reverse order up to maxLength from prefix cells', function () {
218218
const mockCells = [
219219
createNotebookCell(createMockDocument('First cell content')),
220220
createNotebookCell(createMockDocument('Second cell content')),
221221
createNotebookCell(createMockDocument('Third cell content')),
222222
]
223223

224-
const result = EditorContext.extractPrefixCellsContext(mockCells, 100, 'python')
224+
const result = EditorContext.extractCellsSliceContext(mockCells, 100, 'python', false)
225225
assert.strictEqual(result, 'First cell content\nSecond cell content\nThird cell content\n')
226226
})
227227

228-
it('Should respect maxLength parameter', function () {
228+
it('Should extract content from cells in reverse order up to maxLength from suffix cells', function () {
229+
const mockCells = [
230+
createNotebookCell(createMockDocument('First cell content')),
231+
createNotebookCell(createMockDocument('Second cell content')),
232+
createNotebookCell(createMockDocument('Third cell content')),
233+
]
234+
235+
const result = EditorContext.extractCellsSliceContext(mockCells, 100, 'python', true)
236+
assert.strictEqual(result, 'First cell content\nSecond cell content\nThird cell content\n')
237+
})
238+
239+
it('Should respect maxLength parameter from prefix cells', function () {
229240
const mockCells = [
230241
createNotebookCell(createMockDocument('First')),
231242
createNotebookCell(createMockDocument('Second')),
232243
createNotebookCell(createMockDocument('Third')),
233244
createNotebookCell(createMockDocument('Fourth')),
234245
]
235-
236-
const result = EditorContext.extractPrefixCellsContext(mockCells, 15, 'python')
246+
// Should only include part of second cell and the last two cells
247+
const result = EditorContext.extractCellsSliceContext(mockCells, 15, 'python', false)
237248
assert.strictEqual(result, 'd\nThird\nFourth\n')
238249
})
239250

240-
it('Should handle empty cells array', function () {
241-
const result = EditorContext.extractPrefixCellsContext([], 100, '')
251+
it('Should respect maxLength parameter from suffix cells', function () {
252+
const mockCells = [
253+
createNotebookCell(createMockDocument('First')),
254+
createNotebookCell(createMockDocument('Second')),
255+
createNotebookCell(createMockDocument('Third')),
256+
createNotebookCell(createMockDocument('Fourth')),
257+
]
258+
259+
// Should only include first cell and part of second cell
260+
const result = EditorContext.extractCellsSliceContext(mockCells, 15, 'python', true)
261+
assert.strictEqual(result, 'First\nSecond\nTh')
262+
})
263+
264+
it('Should handle empty cells array from prefix cells', function () {
265+
const result = EditorContext.extractCellsSliceContext([], 100, 'python', false)
242266
assert.strictEqual(result, '')
243267
})
244268

245-
it('Should add python comments to markdown cells', function () {
246-
const mockCells = [
247-
createNotebookCell(createMockDocument('# Heading\nThis is markdown'), vscode.NotebookCellKind.Markup),
248-
createNotebookCell(createMockDocument('def example():\n return "test"')),
249-
]
250-
const result = EditorContext.extractPrefixCellsContext(mockCells, 100, 'python')
251-
assert.strictEqual(result, '# # Heading\n# This is markdown\ndef example():\n return "test"\n')
269+
it('Should handle empty cells array from suffix cells', function () {
270+
const result = EditorContext.extractCellsSliceContext([], 100, 'python', true)
271+
assert.strictEqual(result, '')
252272
})
253273

254-
it('Should add java comments to markdown and python cells when language is java', function () {
274+
it('Should add python comments to markdown prefix cells', function () {
255275
const mockCells = [
256276
createNotebookCell(createMockDocument('# Heading\nThis is markdown'), vscode.NotebookCellKind.Markup),
257277
createNotebookCell(createMockDocument('def example():\n return "test"')),
258278
]
259-
const result = EditorContext.extractPrefixCellsContext(mockCells, 100, 'java')
260-
assert.strictEqual(result, '// # Heading\n// This is markdown\n// def example():\n// return "test"\n')
279+
const result = EditorContext.extractCellsSliceContext(mockCells, 100, 'python', false)
280+
assert.strictEqual(result, '# # Heading\n# This is markdown\ndef example():\n return "test"\n')
261281
})
262282

263-
it('Should handle code cells with different languages', function () {
283+
it('Should add python comments to markdown suffix cells', function () {
264284
const mockCells = [
265-
createNotebookCell(
266-
createMockDocument('println(1 + 1);', 'somefile.ipynb', 'java'),
267-
vscode.NotebookCellKind.Code
268-
),
285+
createNotebookCell(createMockDocument('# Heading\nThis is markdown'), vscode.NotebookCellKind.Markup),
269286
createNotebookCell(createMockDocument('def example():\n return "test"')),
270287
]
271-
const result = EditorContext.extractPrefixCellsContext(mockCells, 100, 'python')
272-
assert.strictEqual(result, '# println(1 + 1);\ndef example():\n return "test"\n')
273-
})
274-
})
275288

276-
describe('extractSuffixCellsContext', function () {
277-
it('Should extract content from cells in order up to maxLength', function () {
278-
const mockCells = [
279-
createNotebookCell(createMockDocument('First cell content')),
280-
createNotebookCell(createMockDocument('Second cell content')),
281-
createNotebookCell(createMockDocument('Third cell content')),
282-
]
283-
284-
const result = EditorContext.extractSuffixCellsContext(mockCells, 100, 'python')
285-
assert.strictEqual(result, 'First cell content\nSecond cell content\nThird cell content\n')
289+
const result = EditorContext.extractCellsSliceContext(mockCells, 100, 'python', true)
290+
assert.strictEqual(result, '# # Heading\n# This is markdown\ndef example():\n return "test"\n')
286291
})
287292

288-
it('Should respect maxLength parameter', function () {
293+
it('Should add java comments to markdown and python prefix cells when language is java', function () {
289294
const mockCells = [
290-
createNotebookCell(createMockDocument('First')),
291-
createNotebookCell(createMockDocument('Second')),
292-
createNotebookCell(createMockDocument('Third')),
293-
createNotebookCell(createMockDocument('Fourth')),
295+
createNotebookCell(createMockDocument('# Heading\nThis is markdown'), vscode.NotebookCellKind.Markup),
296+
createNotebookCell(createMockDocument('def example():\n return "test"')),
294297
]
295-
296-
// Should only include first cell and part of second cell
297-
const result = EditorContext.extractSuffixCellsContext(mockCells, 15, 'plaintext')
298-
assert.strictEqual(result, 'First\nSecond\nTh')
299-
})
300-
301-
it('Should handle empty cells array', function () {
302-
const result = EditorContext.extractSuffixCellsContext([], 100, 'plaintext')
303-
assert.strictEqual(result, '')
298+
const result = EditorContext.extractCellsSliceContext(mockCells, 100, 'java', false)
299+
assert.strictEqual(result, '// # Heading\n// This is markdown\n// def example():\n// return "test"\n')
304300
})
305301

306-
it('Should add python comments to markdown cells', function () {
302+
it('Should add java comments to markdown and python suffix cells when language is java', function () {
307303
const mockCells = [
308304
createNotebookCell(createMockDocument('# Heading\nThis is markdown'), vscode.NotebookCellKind.Markup),
309-
createNotebookCell(createMockDocument('def example():\n return "test"')),
305+
createNotebookCell(createMockDocument('println(1 + 1);', 'somefile.ipynb', 'java')),
310306
]
311307

312-
const result = EditorContext.extractSuffixCellsContext(mockCells, 100, 'python')
313-
assert.strictEqual(result, '# # Heading\n# This is markdown\ndef example():\n return "test"\n')
308+
const result = EditorContext.extractCellsSliceContext(mockCells, 100, 'java', true)
309+
assert.strictEqual(result, '// # Heading\n// This is markdown\nprintln(1 + 1);\n')
314310
})
315311

316-
it('Should add java comments to markdown cells', function () {
312+
it('Should handle code prefix cells with different languages', function () {
317313
const mockCells = [
318-
createNotebookCell(createMockDocument('# Heading\nThis is markdown'), vscode.NotebookCellKind.Markup),
319314
createNotebookCell(
320315
createMockDocument('println(1 + 1);', 'somefile.ipynb', 'java'),
321316
vscode.NotebookCellKind.Code
322317
),
318+
createNotebookCell(createMockDocument('def example():\n return "test"')),
323319
]
324-
325-
const result = EditorContext.extractSuffixCellsContext(mockCells, 100, 'java')
326-
assert.strictEqual(result, '// # Heading\n// This is markdown\nprintln(1 + 1);\n')
320+
const result = EditorContext.extractCellsSliceContext(mockCells, 100, 'python', false)
321+
assert.strictEqual(result, '# println(1 + 1);\ndef example():\n return "test"\n')
327322
})
328323

329-
it('Should handle code cells with different languages', function () {
324+
it('Should handle code suffix cells with different languages', function () {
330325
const mockCells = [
331326
createNotebookCell(
332327
createMockDocument('println(1 + 1);', 'somefile.ipynb', 'java'),
333328
vscode.NotebookCellKind.Code
334329
),
335330
createNotebookCell(createMockDocument('def example():\n return "test"')),
336331
]
337-
const result = EditorContext.extractSuffixCellsContext(mockCells, 100, 'python')
332+
const result = EditorContext.extractCellsSliceContext(mockCells, 100, 'python', true)
338333
assert.strictEqual(result, '# println(1 + 1);\ndef example():\n return "test"\n')
339334
})
340335
})

packages/core/src/codewhisperer/util/editorContext.ts

Lines changed: 23 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -51,45 +51,33 @@ export function extractSingleCellContext(cell: vscode.NotebookCell, referenceLan
5151
return cellText
5252
}
5353

54-
export function extractPrefixCellsContext(
54+
export function extractCellsSliceContext(
5555
cells: vscode.NotebookCell[],
5656
maxLength: number,
57-
referenceLanguage: string
57+
referenceLanguage: string,
58+
fromStart: boolean
5859
): string {
59-
const output: string[] = []
60-
for (let i = cells.length - 1; i >= 0; i--) {
61-
let cellText = addNewlineIfMissing(extractSingleCellContext(cells[i], referenceLanguage))
62-
if (cellText.length > 0) {
63-
if (cellText.length >= maxLength) {
64-
output.unshift(cellText.substring(cellText.length - maxLength))
65-
break
66-
}
67-
output.unshift(cellText)
68-
maxLength -= cellText.length
69-
}
60+
let output: string[] = []
61+
if (!fromStart) {
62+
cells = cells.reverse()
7063
}
71-
return output.join('')
72-
}
73-
74-
export function extractSuffixCellsContext(
75-
cells: vscode.NotebookCell[],
76-
maxLength: number,
77-
referenceLanguage: string
78-
): string {
79-
const output: string[] = []
80-
for (let i = 0; i < cells.length; i++) {
81-
let cellText = addNewlineIfMissing(extractSingleCellContext(cells[i], referenceLanguage))
64+
cells.some((cell) => {
65+
let cellText = addNewlineIfMissing(extractSingleCellContext(cell, referenceLanguage))
8266
if (cellText.length > 0) {
83-
if (!cellText.endsWith('\n')) {
84-
cellText += '\n'
85-
}
8667
if (cellText.length >= maxLength) {
87-
output.push(cellText.substring(0, maxLength))
88-
break
68+
if (fromStart) {
69+
output.push(cellText.substring(0, maxLength))
70+
} else {
71+
output.push(cellText.substring(cellText.length - maxLength))
72+
}
73+
return true
8974
}
9075
output.push(cellText)
9176
maxLength -= cellText.length
9277
}
78+
})
79+
if (!fromStart) {
80+
output = output.reverse()
9381
}
9482
return output.join('')
9583
}
@@ -136,21 +124,23 @@ export function extractContextForCodeWhisperer(editor: vscode.TextEditor): codew
136124

137125
// Extract text from prior cells if there is enough room in left file context
138126
if (caretLeftFileContext.length < CodeWhispererConstants.charactersLimit - 1) {
139-
const leftCellsText = extractPrefixCellsContext(
127+
const leftCellsText = extractCellsSliceContext(
140128
allCells.slice(0, cellIndex),
141129
CodeWhispererConstants.charactersLimit - (caretLeftFileContext.length + 1),
142-
languageName
130+
languageName,
131+
true
143132
)
144133
if (leftCellsText.length > 0) {
145134
caretLeftFileContext = addNewlineIfMissing(leftCellsText) + caretLeftFileContext
146135
}
147136
}
148137
// Extract text from subsequent cells if there is enough room in right file context
149138
if (caretRightFileContext.length < CodeWhispererConstants.charactersLimit - 1) {
150-
const rightCellsText = extractSuffixCellsContext(
139+
const rightCellsText = extractCellsSliceContext(
151140
allCells.slice(cellIndex + 1),
152141
CodeWhispererConstants.charactersLimit - (caretRightFileContext.length + 1),
153-
languageName
142+
languageName,
143+
false
154144
)
155145
if (rightCellsText.length > 0) {
156146
caretRightFileContext = addNewlineIfMissing(caretRightFileContext) + rightCellsText

0 commit comments

Comments
 (0)