Skip to content

Commit 1d572b5

Browse files
authored
Experiment table open to the side (#1796)
* Add Open-to-the-side option to table header context-menu * Remove commented code * Add test for the Open-to-the-side message handler * Add a telemetry event for the open file message * Refactor array utils * Clean the file segment of column paths before using them * Limit Hide column option to leaves only
1 parent d6ea272 commit 1d572b5

File tree

10 files changed

+129
-35
lines changed

10 files changed

+129
-35
lines changed

extension/src/experiments/columns/paths.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,13 @@ export const splitColumnPath = (path: string) => {
4747
if (!fileSegment) {
4848
return [baseSegment]
4949
}
50+
const cleanFileSegment = fileSegment.replace(/_\d+$/g, '')
5051
if (!paramPath) {
51-
return [baseSegment, fileSegment]
52+
return [baseSegment, cleanFileSegment]
5253
}
5354
return [
5455
baseSegment,
55-
fileSegment,
56+
cleanFileSegment,
5657
...paramPath.split(METRIC_PARAM_SEPARATOR).map(decodeColumn)
5758
]
5859
}

extension/src/experiments/index.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { Event, EventEmitter, Memento, window } from 'vscode'
1+
import { join } from 'path'
2+
import { Event, EventEmitter, Memento, Uri, ViewColumn, window } from 'vscode'
23
import { ExperimentsModel } from './model'
34
import { pickExperiments } from './model/quickPicks'
45
import { pickAndModifyParams } from './model/queue/quickPick'
@@ -15,6 +16,7 @@ import { ExperimentsData } from './data'
1516
import { askToDisableAutoApplyFilters } from './toast'
1617
import { Experiment, ColumnType, TableData } from './webview/contract'
1718
import { SortDefinition } from './model/sortBy'
19+
import { splitColumnPath } from './columns/paths'
1820
import { ResourceLocator } from '../resourceLocator'
1921
import {
2022
AvailableCommands,
@@ -380,6 +382,18 @@ export class Experiments extends BaseRepository<TableData> {
380382
)
381383
}
382384

385+
private async openParamsFileToTheSide(path: string) {
386+
const [, fileSegment] = splitColumnPath(path)
387+
await window.showTextDocument(Uri.file(join(this.dvcRoot, fileSegment)), {
388+
viewColumn: ViewColumn.Beside
389+
})
390+
sendTelemetryEvent(
391+
EventName.VIEWS_EXPERIMENTS_TABLE_OPEN_PARAMS_FILE,
392+
{ path },
393+
undefined
394+
)
395+
}
396+
383397
private setupInitialData() {
384398
const waitForInitialData = this.dispose.track(
385399
this.onDidChangeExperiments(() => {
@@ -440,6 +454,8 @@ export class Experiments extends BaseRepository<TableData> {
440454
return this.setExperimentStatus(message.payload)
441455
case MessageFromWebviewType.HIDE_EXPERIMENTS_TABLE_COLUMN:
442456
return this.hideTableColumn(message.payload)
457+
case MessageFromWebviewType.OPEN_PARAMS_FILE_TO_THE_SIDE:
458+
return this.openParamsFileToTheSide(message.payload)
443459
case MessageFromWebviewType.SORT_COLUMN:
444460
return this.addColumnSort(message.payload)
445461
case MessageFromWebviewType.REMOVE_COLUMN_SORT:

extension/src/telemetry/constants.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ export const EventName = Object.assign(
3737
VIEWS_EXPERIMENTS_TABLE_FOCUS_CHANGED:
3838
'views.experimentsTable.focusChanged',
3939
VIEWS_EXPERIMENTS_TABLE_HIDE_COLUMN: 'views.experimentsTable.columnHidden',
40+
VIEWS_EXPERIMENTS_TABLE_OPEN_PARAMS_FILE:
41+
'views.experimentsTable.paramsFileOpened',
4042
VIEWS_EXPERIMENTS_TABLE_REMOVE_COLUMN_SORT:
4143
'views.experimentsTable.columnSortRemoved',
4244
VIEWS_EXPERIMENTS_TABLE_RESIZE_COLUMN:
@@ -196,6 +198,9 @@ export interface IEventNamePropertyMapping {
196198
path: string
197199
}
198200
[EventName.VIEWS_EXPERIMENTS_TABLE_SELECT_COLUMNS]: undefined
201+
[EventName.VIEWS_EXPERIMENTS_TABLE_OPEN_PARAMS_FILE]: {
202+
path: string
203+
}
199204

200205
[EventName.VIEWS_PLOTS_CLOSED]: undefined
201206
[EventName.VIEWS_PLOTS_CREATED]: undefined

extension/src/test/suite/experiments/index.test.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import {
88
commands,
99
workspace,
1010
Uri,
11-
QuickPickItem
11+
QuickPickItem,
12+
ViewColumn
1213
} from 'vscode'
1314
import { buildExperiments } from './util'
1415
import { Disposable } from '../../../extension'
@@ -390,6 +391,50 @@ suite('Experiments Test Suite', () => {
390391
)
391392
})
392393

394+
it('should be able to handle a message to open the source params file from a column path', async () => {
395+
const { experiments } = setupExperimentsAndMockCommands()
396+
397+
const mockShowTextDocument = stub(window, 'showTextDocument')
398+
const webview = await experiments.showWebview()
399+
const mockMessageReceived = getMessageReceivedEmitter(webview)
400+
const mockColumnId = 'params:params.yaml_5'
401+
402+
mockMessageReceived.fire({
403+
payload: mockColumnId,
404+
type: MessageFromWebviewType.OPEN_PARAMS_FILE_TO_THE_SIDE
405+
})
406+
407+
expect(mockShowTextDocument).to.be.calledOnce
408+
expect(mockShowTextDocument).to.be.calledWithExactly(
409+
Uri.file(join(dvcDemoPath, 'params.yaml')),
410+
{
411+
viewColumn: ViewColumn.Beside
412+
}
413+
)
414+
})
415+
416+
it('should be able to handle a message to open different params files than the default one', async () => {
417+
const { experiments } = setupExperimentsAndMockCommands()
418+
419+
const mockShowTextDocument = stub(window, 'showTextDocument')
420+
const webview = await experiments.showWebview()
421+
const mockMessageReceived = getMessageReceivedEmitter(webview)
422+
const mockColumnId = 'params:params_alt.json_5:nested1.nested2'
423+
424+
mockMessageReceived.fire({
425+
payload: mockColumnId,
426+
type: MessageFromWebviewType.OPEN_PARAMS_FILE_TO_THE_SIDE
427+
})
428+
429+
expect(mockShowTextDocument).to.be.calledOnce
430+
expect(mockShowTextDocument).to.be.calledWithExactly(
431+
Uri.file(join(dvcDemoPath, 'params_alt.json')),
432+
{
433+
viewColumn: ViewColumn.Beside
434+
}
435+
)
436+
})
437+
393438
it('should be able to handle a message to apply an experiment to workspace', async () => {
394439
const { experiments, mockExecuteCommand } =
395440
setupExperimentsAndMockCommands()

extension/src/webview/contract.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export enum MessageFromWebviewType {
1414
INITIALIZED = 'initialized',
1515
APPLY_EXPERIMENT_TO_WORKSPACE = 'apply-experiment-to-workspace',
1616
CREATE_BRANCH_FROM_EXPERIMENT = 'create-branch-from-experiment',
17+
OPEN_PARAMS_FILE_TO_THE_SIDE = 'open-params-file-to-the-side',
1718
REMOVE_COLUMN_SORT = 'remove-column-sort',
1819
REMOVE_EXPERIMENT = 'remove-experiment',
1920
RENAME_SECTION = 'rename-section',
@@ -68,6 +69,10 @@ export type MessageFromWebview =
6869
type: MessageFromWebviewType.HIDE_EXPERIMENTS_TABLE_COLUMN
6970
payload: string
7071
}
72+
| {
73+
type: MessageFromWebviewType.OPEN_PARAMS_FILE_TO_THE_SIDE
74+
payload: string
75+
}
7176
| {
7277
type: MessageFromWebviewType.APPLY_EXPERIMENT_TO_WORKSPACE
7378
payload: string

webview/src/experiments/components/table/Row.tsx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { sendMessage } from '../../../shared/vscode'
99
import { ContextMenu } from '../../../shared/components/contextMenu/ContextMenu'
1010
import { MessagesMenu } from '../../../shared/components/messagesMenu/MessagesMenu'
1111
import { MessagesMenuOptionProps } from '../../../shared/components/messagesMenu/MessagesMenuOption'
12+
import { pushIf } from '../../../util/array'
1213

1314
const getExperimentTypeClass = ({ running, queued, selected }: Experiment) => {
1415
if (running) {
@@ -52,10 +53,7 @@ export const RowContextMenu: React.FC<RowProp> = ({
5253
const contextMenuOptions = React.useMemo(() => {
5354
const menuOptions: MessagesMenuOptionProps[] = []
5455

55-
const pushIf = (condition: boolean, options: MessagesMenuOptionProps[]) =>
56-
condition && menuOptions.push(...options)
57-
58-
pushIf(!queued && !isWorkspace && depth > 0, [
56+
pushIf(menuOptions, !queued && !isWorkspace && depth > 0, [
5957
experimentMenuOption(
6058
id,
6159
'Apply to Workspace',
@@ -68,7 +66,7 @@ export const RowContextMenu: React.FC<RowProp> = ({
6866
)
6967
])
7068

71-
pushIf(depth === 1, [
69+
pushIf(menuOptions, depth === 1, [
7270
experimentMenuOption(
7371
id,
7472
'Remove',
@@ -78,23 +76,23 @@ export const RowContextMenu: React.FC<RowProp> = ({
7876

7977
const isNotCheckpoint = depth <= 1 || isWorkspace
8078

81-
pushIf(isNotCheckpoint, [
79+
pushIf(menuOptions, isNotCheckpoint, [
8280
experimentMenuOption(
8381
id,
8482
projectHasCheckpoints ? 'Modify and Resume' : 'Modify and Run',
8583
MessageFromWebviewType.VARY_EXPERIMENT_PARAMS_AND_RUN
8684
)
8785
])
8886

89-
pushIf(isNotCheckpoint && projectHasCheckpoints, [
87+
pushIf(menuOptions, isNotCheckpoint && projectHasCheckpoints, [
9088
experimentMenuOption(
9189
id,
9290
'Modify, Reset and Run',
9391
MessageFromWebviewType.VARY_EXPERIMENT_PARAMS_RESET_AND_RUN
9492
)
9593
])
9694

97-
pushIf(isNotCheckpoint, [
95+
pushIf(menuOptions, isNotCheckpoint, [
9896
experimentMenuOption(
9997
id,
10098
'Modify and Queue',

webview/src/experiments/components/table/TableHeader.tsx

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
OnDrop
2222
} from '../../../shared/components/dragDrop/DragDropWorkbench'
2323
import { MessagesMenu } from '../../../shared/components/messagesMenu/MessagesMenu'
24+
import { MessagesMenuOptionProps } from '../../../shared/components/messagesMenu/MessagesMenuOption'
2425

2526
export const ColumnDragHandle: React.FC<{
2627
disabled: boolean
@@ -197,6 +198,31 @@ export const TableHeader: React.FC<TableHeaderProps> = ({
197198
})
198199
}
199200

201+
const contextMenuOptions: MessagesMenuOptionProps[] = React.useMemo(() => {
202+
const menuOptions: MessagesMenuOptionProps[] = [
203+
{
204+
hidden: !!column.headers,
205+
id: 'hide-column',
206+
label: 'Hide Column',
207+
message: {
208+
payload: column.id,
209+
type: MessageFromWebviewType.HIDE_EXPERIMENTS_TABLE_COLUMN
210+
}
211+
},
212+
{
213+
hidden: column.group !== ColumnType.PARAMS,
214+
id: 'open-to-the-side',
215+
label: 'Open to the Side',
216+
message: {
217+
payload: column.id,
218+
type: MessageFromWebviewType.OPEN_PARAMS_FILE_TO_THE_SIDE
219+
}
220+
}
221+
]
222+
223+
return menuOptions
224+
}, [column])
225+
200226
return (
201227
<TableHeaderCell
202228
column={column}
@@ -206,28 +232,21 @@ export const TableHeader: React.FC<TableHeaderProps> = ({
206232
onDragOver={onDragOver}
207233
onDragStart={onDragStart}
208234
onDrop={onDrop}
209-
menuDisabled={!isSortable}
235+
menuDisabled={!isSortable && column.group !== ColumnType.PARAMS}
210236
menuContent={
211237
<div>
212-
<SortPicker
213-
sortOrder={sortOrder}
214-
setSelectedOrder={order => {
215-
setColumnSort(order)
216-
}}
217-
/>
218-
<VSCodeDivider />
219-
<MessagesMenu
220-
options={[
221-
{
222-
id: 'hide-column',
223-
label: 'Hide Column',
224-
message: {
225-
payload: column.id,
226-
type: MessageFromWebviewType.HIDE_EXPERIMENTS_TABLE_COLUMN
227-
}
228-
}
229-
]}
230-
/>
238+
{isSortable && (
239+
<div>
240+
<SortPicker
241+
sortOrder={sortOrder}
242+
setSelectedOrder={order => {
243+
setColumnSort(order)
244+
}}
245+
/>
246+
<VSCodeDivider />
247+
</div>
248+
)}
249+
<MessagesMenu options={contextMenuOptions} />
231250
</div>
232251
}
233252
/>

webview/src/shared/components/messagesMenu/MessagesMenu.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ export interface MessagesMenuProps {
1010

1111
export const MessagesMenu: React.FC<MessagesMenuProps> = ({ options }) => (
1212
<div role="menu">
13-
{options.map((option, i) => (
14-
<MessagesMenuOption key={option.id} {...option} index={i} />
15-
))}
13+
{options
14+
.filter(({ hidden }) => !hidden)
15+
.map((option, i) => (
16+
<MessagesMenuOption key={option.id} {...option} index={i} />
17+
))}
1618
</div>
1719
)

webview/src/shared/components/messagesMenu/MessagesMenuOption.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export interface MessagesMenuOptionProps {
77
id: string
88
label: string
99
message: MessageFromWebview
10+
hidden?: boolean
1011
}
1112

1213
interface MessagesMenuOptionAllProps extends MessagesMenuOptionProps {

webview/src/util/array.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const pushIf = <T>(array: T[], condition: boolean, elements: T[]) =>
2+
condition && array.push(...elements)

0 commit comments

Comments
 (0)