Skip to content

Commit c9d6887

Browse files
authored
Remove 'breakpoint' from the top of a cell when debugging. (#9004)
* Remove 'breakpoint' from the top of a cell when debugging. * Fix build error * Another weird build error only in the unit tests. * Fix problem if no source * Fix more typescript problems with unit tests * Try again * One more * Fix crash when invalid default cell marker
1 parent a1dc04c commit c9d6887

12 files changed

Lines changed: 137 additions & 41 deletions

File tree

news/2 Fixes/8260.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
'breakpoint' line shows up in the interactive window when debugging a cell.

src/client/datascience/cellMatcher.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,28 @@ export class CellMatcher {
1212
private markdownMatchRegEx: RegExp;
1313
private codeExecRegEx: RegExp;
1414
private markdownExecRegEx: RegExp;
15+
private defaultCellMarker: string;
16+
private defaultCellMarkerExec: RegExp;
1517

1618
constructor(settings?: IDataScienceSettings) {
1719
this.codeMatchRegEx = this.createRegExp(settings ? settings.codeRegularExpression : undefined, RegExpValues.PythonCellMarker);
1820
this.markdownMatchRegEx = this.createRegExp(settings ? settings.markdownRegularExpression : undefined, RegExpValues.PythonMarkdownCellMarker);
1921
this.codeExecRegEx = new RegExp(`${this.codeMatchRegEx.source}(.*)`);
2022
this.markdownExecRegEx = new RegExp(`${this.markdownMatchRegEx.source}(.*)`);
23+
this.defaultCellMarker = settings?.defaultCellMarker ? settings.defaultCellMarker : '# %%';
24+
this.defaultCellMarkerExec = this.createRegExp(`${this.defaultCellMarker}(.*)`, /# %%(.*)/);
2125
}
2226

2327
public isCell(code: string): boolean {
24-
return this.codeMatchRegEx.test(code) || this.markdownMatchRegEx.test(code);
28+
return this.isCode(code) || this.isMarkdown(code);
2529
}
2630

2731
public isMarkdown(code: string): boolean {
2832
return this.markdownMatchRegEx.test(code);
2933
}
3034

3135
public isCode(code: string): boolean {
32-
return this.codeMatchRegEx.test(code);
36+
return this.codeMatchRegEx.test(code) || code.trim() === this.defaultCellMarker;
3337
}
3438

3539
public getCellType(code: string): string {
@@ -48,7 +52,10 @@ export class CellMatcher {
4852

4953
public exec(code: string): string | undefined {
5054
let result: RegExpExecArray | null = null;
51-
if (this.codeMatchRegEx.test(code)) {
55+
if (this.defaultCellMarkerExec.test(code)) {
56+
this.defaultCellMarkerExec.lastIndex = -1;
57+
result = this.defaultCellMarkerExec.exec(code);
58+
} else if (this.codeMatchRegEx.test(code)) {
5259
this.codeExecRegEx.lastIndex = -1;
5360
result = this.codeExecRegEx.exec(code);
5461
} else if (this.markdownMatchRegEx.test(code)) {

src/datascience-ui/history-react/redux/reducers/creation.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,13 @@ export namespace Creation {
4141
if (cellVM.inputBlockOpen !== expanded && cellVM.inputBlockCollapseNeeded && cellVM.inputBlockShow) {
4242
if (expanded) {
4343
// Expand the cell
44-
const newText = extractInputText(cellVM.cell, settings);
44+
const newText = extractInputText(cellVM, settings);
4545

4646
newCellVM.inputBlockOpen = true;
4747
newCellVM.inputBlockText = newText;
4848
} else {
4949
// Collapse the cell
50-
let newText = extractInputText(cellVM.cell, settings);
50+
let newText = extractInputText(cellVM, settings);
5151
if (newText.length > 0) {
5252
newText = newText.split('\n', 1)[0];
5353
newText = newText.slice(0, 255); // Slice to limit length, slicing past length is fine
@@ -65,14 +65,14 @@ export namespace Creation {
6565
return cellVM;
6666
}
6767

68-
export function prepareCellVM(cell: ICell, settings?: IDataScienceExtraSettings): ICellViewModel {
69-
let cellVM: ICellViewModel = createCellVM(cell, settings, false);
68+
export function prepareCellVM(cell: ICell, mainState: IMainState): ICellViewModel {
69+
let cellVM: ICellViewModel = createCellVM(cell, mainState.settings, false, mainState.debugging);
7070

71-
const visible = settings ? settings.showCellInputCode : false;
72-
const expanded = !settings?.collapseCellInputCodeByDefault;
71+
const visible = mainState.settings ? mainState.settings.showCellInputCode : false;
72+
const expanded = !mainState.settings?.collapseCellInputCodeByDefault;
7373

7474
// Set initial cell visibility and collapse
75-
cellVM = alterCellVM(cellVM, settings, visible, expanded);
75+
cellVM = alterCellVM(cellVM, mainState.settings, visible, expanded);
7676
cellVM.hasBeenRun = true;
7777

7878
return cellVM;
@@ -88,7 +88,7 @@ export namespace Creation {
8888
arg.queueAction(createPostableAction(
8989
InteractiveWindowMessages.AddCell,
9090
{
91-
fullText: extractInputText(cellVM.cell, result.settings),
91+
fullText: extractInputText(cellVM, result.settings),
9292
currentText: cellVM.inputBlockText,
9393
cell: cellVM.cell
9494
}));

src/datascience-ui/history-react/redux/reducers/execution.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ export namespace Execution {
9999
}
100100

101101
// Update input controls (always show expanded since we just edited it.)
102-
newCell = createCellVM(newCell.cell, arg.prevState.settings, false);
102+
newCell = createCellVM(newCell.cell, arg.prevState.settings, false, false);
103103
const collapseInputs = arg.prevState.settings ? arg.prevState.settings.collapseCellInputCodeByDefault : false;
104104
newCell = Creation.alterCellVM(newCell, arg.prevState.settings, true, !collapseInputs);
105105
newCell.useQuickEdit = false;

src/datascience-ui/interactive-common/mainState.ts

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export interface ICellViewModel {
3636
scrollCount: number;
3737
cursorPos: CursorPos;
3838
hasBeenRun: boolean;
39+
runDuringDebug?: boolean;
3940
}
4041

4142
export type IMainState = {
@@ -172,7 +173,8 @@ export function createEditableCellVM(executionCount: number): ICellViewModel {
172173
};
173174
}
174175

175-
export function extractInputText(inputCell: ICell, settings: IDataScienceSettings | undefined): string {
176+
export function extractInputText(inputCellVM: ICellViewModel, settings: IDataScienceSettings | undefined): string {
177+
const inputCell = inputCellVM.cell;
176178
let source: string[] = [];
177179
if (inputCell.data.source) {
178180
source = splitMultilineString(cloneDeep(inputCell.data.source));
@@ -192,35 +194,49 @@ export function extractInputText(inputCell: ICell, settings: IDataScienceSetting
192194
}
193195
}
194196

197+
// Eliminate breakpoint on the front if we're debugging and breakpoints are expected to be prefixed
198+
if (source.length > 0 && inputCellVM.runDuringDebug && (!settings || settings.stopOnFirstLineWhileDebugging)) {
199+
if (source[0].trim() === 'breakpoint()') {
200+
source.splice(0, 1);
201+
}
202+
}
203+
195204
return concatMultilineStringInput(source);
196205
}
197206

198-
export function createCellVM(inputCell: ICell, settings: IDataScienceSettings | undefined, editable: boolean): ICellViewModel {
199-
let inputLinesCount = 0;
200-
const inputText = inputCell.data.cell_type === 'code' ? extractInputText(inputCell, settings) : '';
201-
if (inputText) {
202-
inputLinesCount = inputText.split('\n').length;
203-
}
204-
205-
return {
207+
export function createCellVM(inputCell: ICell, settings: IDataScienceSettings | undefined, editable: boolean, runDuringDebug: boolean): ICellViewModel {
208+
const vm = {
206209
cell: inputCell,
207210
editable,
208211
inputBlockOpen: true,
209212
inputBlockShow: true,
210-
inputBlockText: inputText,
211-
inputBlockCollapseNeeded: (inputLinesCount > 1),
213+
inputBlockText: '',
214+
inputBlockCollapseNeeded: false,
212215
selected: false,
213216
focused: false,
214217
cursorPos: CursorPos.Current,
215218
hasBeenRun: false,
216-
scrollCount: 0
219+
scrollCount: 0,
220+
runDuringDebug
217221
};
222+
223+
// Update the input text
224+
let inputLinesCount = 0;
225+
const inputText = inputCell.data.cell_type === 'code' ? extractInputText(vm, settings) : '';
226+
if (inputText) {
227+
inputLinesCount = inputText.split('\n').length;
228+
}
229+
230+
vm.inputBlockText = inputText;
231+
vm.inputBlockCollapseNeeded = inputLinesCount > 1;
232+
233+
return vm;
218234
}
219235

220236
function generateTestVMs(filePath: string, editable: boolean): ICellViewModel[] {
221237
const cells = generateTestCells(filePath, 10);
222238
return cells.map((cell: ICell) => {
223-
const vm = createCellVM(cell, undefined, editable);
239+
const vm = createCellVM(cell, undefined, editable, false);
224240
vm.useQuickEdit = false;
225241
vm.hasBeenRun = true;
226242
return vm;

src/datascience-ui/interactive-common/redux/reducers/helpers.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,13 @@ export namespace Helpers {
3939
return undefined;
4040
}
4141

42-
export function updateOrAdd<T>(arg: CommonReducerArg<T, ICell>, generateVM: (cell: ICell, settings?: IDataScienceExtraSettings) => ICellViewModel): IMainState {
42+
// This function is because the unit test typescript compiler can't handle ICell.metadata
43+
// tslint:disable-next-line: no-any
44+
export function asCellViewModel(cvm: any): ICellViewModel {
45+
return cvm as ICellViewModel;
46+
}
47+
48+
export function updateOrAdd<T>(arg: CommonReducerArg<T, ICell>, generateVM: (cell: ICell, mainState: IMainState) => ICellViewModel): IMainState {
4349
// First compute new execution count.
4450
const newExecutionCount = arg.payload.data.execution_count ?
4551
Math.max(arg.prevState.currentExecutionCount, parseInt(arg.payload.data.execution_count.toString(), 10)) :
@@ -69,7 +75,7 @@ export namespace Helpers {
6975

7076
// Prevent updates to the source, as its possible we have recieved a response for a cell execution
7177
// and the user has updated the cell text since then.
72-
newVMs[index] = {
78+
const newVM = {
7379
...newVMs[index],
7480
cell: {
7581
...newVMs[index].cell,
@@ -80,6 +86,7 @@ export namespace Helpers {
8086
}
8187
}
8288
};
89+
newVMs[index] = asCellViewModel(newVM);
8390

8491
return {
8592
...arg.prevState,
@@ -88,7 +95,7 @@ export namespace Helpers {
8895
};
8996
} else {
9097
// This is an entirely new cell (it may have started out as finished)
91-
const newVM = generateVM(arg.payload, arg.prevState.settings);
98+
const newVM = generateVM(arg.payload, arg.prevState);
9299
const newVMs = [
93100
...arg.prevState.cellVMs,
94101
newVM];

src/datascience-ui/interactive-common/redux/reducers/transfer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export namespace Transfer {
7777

7878
// Send a message to the other side to jump to a particular cell
7979
if (cellVM) {
80-
arg.queueAction(createPostableAction(InteractiveWindowMessages.CopyCodeCell, { source: extractInputText(cellVM.cell, arg.prevState.settings) }));
80+
arg.queueAction(createPostableAction(InteractiveWindowMessages.CopyCodeCell, { source: extractInputText(cellVM, arg.prevState.settings) }));
8181
}
8282

8383
return arg.prevState;

src/datascience-ui/native-editor/redux/reducers/creation.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ import { NativeEditorReducerArg } from '../mapping';
2424

2525
export namespace Creation {
2626
function prepareCellVM(cell: ICell, hasBeenRun: boolean, settings?: IDataScienceExtraSettings): ICellViewModel {
27-
const cellVM: ICellViewModel = createCellVM(cell, settings, true);
27+
const cellVM: ICellViewModel = createCellVM(cell, settings, true, false);
2828

2929
// Set initial cell visibility and collapse
3030
cellVM.editable = true;
3131

3232
// Always have the cell input text open
33-
const newText = extractInputText(cellVM.cell, settings);
33+
const newText = extractInputText(cellVM, settings);
3434

3535
cellVM.inputBlockOpen = true;
3636
cellVM.inputBlockText = newText;
@@ -124,15 +124,15 @@ export namespace Creation {
124124
}
125125

126126
export function startCell(arg: NativeEditorReducerArg<ICell>): IMainState {
127-
return Helpers.updateOrAdd(arg, (c: ICell, s?: IDataScienceExtraSettings) => prepareCellVM(c, true, s));
127+
return Helpers.updateOrAdd(arg, (c: ICell, s: IMainState) => prepareCellVM(c, true, s.settings));
128128
}
129129

130130
export function updateCell(arg: NativeEditorReducerArg<ICell>): IMainState {
131-
return Helpers.updateOrAdd(arg, (c: ICell, s?: IDataScienceExtraSettings) => prepareCellVM(c, true, s));
131+
return Helpers.updateOrAdd(arg, (c: ICell, s: IMainState) => prepareCellVM(c, true, s.settings));
132132
}
133133

134134
export function finishCell(arg: NativeEditorReducerArg<ICell>): IMainState {
135-
return Helpers.updateOrAdd(arg, (c: ICell, s?: IDataScienceExtraSettings) => prepareCellVM(c, true, s));
135+
return Helpers.updateOrAdd(arg, (c: ICell, s: IMainState) => prepareCellVM(c, true, s.settings));
136136
}
137137

138138
export function deleteAllCells(arg: NativeEditorReducerArg): IMainState {

src/datascience-ui/native-editor/redux/reducers/effects.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export namespace Effects {
6060
};
6161

6262
// tslint:disable-next-line: no-any
63-
newVMs[index] = (newCell as any); // This is because IMessageCell doesn't fit in here
63+
newVMs[index] = Helpers.asCellViewModel(newCell); // This is because IMessageCell doesn't fit in here
6464

6565
return {
6666
...arg.prevState,

src/datascience-ui/native-editor/redux/reducers/execution.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ export namespace Execution {
3232
if (code && matcher.stripFirstMarker(code).length > 0) {
3333
if (orig.cell.data.cell_type === 'code') {
3434
// Update our input cell to be in progress again and clear outputs
35-
newVMs[pos] = { ...orig, inputBlockText: code, cell: { ...orig.cell, state: CellState.executing, data: { ...orig.cell.data, source: code, outputs: [] } } };
35+
newVMs[pos] = Helpers.asCellViewModel({ ...orig, inputBlockText: code, cell: { ...orig.cell, state: CellState.executing, data: { ...orig.cell.data, source: code, outputs: [] } } });
3636

3737
// Send a message if a code cell
3838
queueAction(createPostableAction(InteractiveWindowMessages.ReExecuteCell, { code, id: orig.cell.id }));
3939
} else {
4040
// Update our input to be our new code
41-
newVMs[pos] = { ...orig, inputBlockText: code, cell: { ...orig.cell, data: { ...orig.cell.data, source: code } } };
41+
newVMs[pos] = Helpers.asCellViewModel({ ...orig, inputBlockText: code, cell: { ...orig.cell, data: { ...orig.cell.data, source: code } } });
4242
}
4343
}
4444

@@ -127,7 +127,7 @@ export namespace Execution {
127127

128128
export function clearAllOutputs(arg: NativeEditorReducerArg): IMainState {
129129
const newList = arg.prevState.cellVMs.map(cellVM => {
130-
return { ...cellVM, cell: { ...cellVM.cell, data: { ...cellVM.cell.data, outputs: [], execution_count: null } } };
130+
return Helpers.asCellViewModel({ ...cellVM, cell: { ...cellVM.cell, data: { ...cellVM.cell.data, outputs: [], execution_count: null } } });
131131
});
132132

133133
arg.queueAction(createPostableAction(InteractiveWindowMessages.ClearAllOutputs));

0 commit comments

Comments
 (0)