Skip to content

Commit d843598

Browse files
authored
Add experiment icons to editor/title when params file is open (#1740)
* add prototype for showing experiment icons in editor/title when params file is open * add variations for all commands * await result of async function * move listener into private method * test and refactor * add workspace focus when params file is open
1 parent 5fdbe92 commit d843598

File tree

13 files changed

+273
-16
lines changed

13 files changed

+273
-16
lines changed

extension/package.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,11 @@
835835
}
836836
],
837837
"editor/title": [
838+
{
839+
"command": "dvc.runExperiment",
840+
"group": "navigation@0",
841+
"when": "dvc.params.fileActive && !dvc.runner.running && dvc.commands.available && !dvc.experiment.checkpoints"
842+
},
838843
{
839844
"command": "dvc.runExperiment",
840845
"group": "navigation@0",
@@ -850,20 +855,40 @@
850855
"group": "navigation@1",
851856
"when": "dvc.experiments.webviewActive && !dvc.runner.running && dvc.commands.available && dvc.experiment.checkpoints"
852857
},
858+
{
859+
"command": "dvc.resumeCheckpointExperiment",
860+
"group": "navigation@1",
861+
"when": "dvc.params.fileActive && !dvc.runner.running && dvc.commands.available && dvc.experiment.checkpoints"
862+
},
853863
{
854864
"command": "dvc.resetAndRunCheckpointExperiment",
855865
"group": "navigation@2",
856866
"when": "dvc.experiments.webviewActive && !dvc.runner.running && dvc.commands.available && dvc.experiment.checkpoints"
857867
},
868+
{
869+
"command": "dvc.resetAndRunCheckpointExperiment",
870+
"group": "navigation@2",
871+
"when": "dvc.params.fileActive && !dvc.runner.running && dvc.commands.available && dvc.experiment.checkpoints"
872+
},
858873
{
859874
"command": "dvc.runQueuedExperiments",
860875
"group": "navigation@3",
861876
"when": "dvc.experiments.webviewActive && !dvc.runner.running && dvc.commands.available"
862877
},
878+
{
879+
"command": "dvc.runQueuedExperiments",
880+
"group": "navigation@3",
881+
"when": "dvc.params.fileActive && !dvc.runner.running && dvc.commands.available"
882+
},
863883
{
864884
"command": "dvc.queueExperiment",
865885
"group": "navigation@4",
866886
"when": "dvc.experiments.webviewActive && !dvc.runner.running && dvc.commands.available"
887+
},
888+
{
889+
"command": "dvc.queueExperiment",
890+
"group": "navigation@4",
891+
"when": "dvc.params.fileActive && !dvc.runner.running && dvc.commands.available"
867892
}
868893
],
869894
"view/item/context": [

extension/src/experiments/columns/collect.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { join } from 'path'
12
import get from 'lodash/get'
23
import { ValueWalkMeta, walkRepo } from './walk'
34
import { joinColumnPath, METRIC_PARAM_SEPARATOR } from './paths'
@@ -10,6 +11,7 @@ import {
1011
ValueTree,
1112
ValueTreeOrError
1213
} from '../../cli/reader'
14+
import { standardizePath } from '../../fileSystem/path'
1315

1416
const getValueType = (value: Value) => {
1517
if (value === null) {
@@ -205,6 +207,16 @@ const collectColumnsChanges = (
205207
}
206208
}
207209

210+
export const collectParamsFiles = (
211+
dvcRoot: string,
212+
data: ExperimentsOutput
213+
): Set<string> => {
214+
const files = Object.keys(data.workspace.baseline.data?.params || {})
215+
.filter(Boolean)
216+
.map(file => standardizePath(join(dvcRoot, file))) as string[]
217+
return new Set(files)
218+
}
219+
208220
const getData = (value: { baseline: ExperimentFieldsOrError }) =>
209221
value.baseline.data || {}
210222

extension/src/experiments/columns/model.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ import {
1414
describe('ColumnsModel', () => {
1515
const exampleDvcRoot = 'test'
1616

17-
it('should return columns that equal the columns fixture when given the output fixture', () => {
17+
it('should return columns that equal the columns fixture when given the output fixture', async () => {
1818
const model = new ColumnsModel('', buildMockMemento())
19-
model.transformAndSet(outputFixture)
19+
await model.transformAndSet(outputFixture)
2020
expect(model.getSelected()).toStrictEqual(columnsFixture)
2121
})
2222

23-
it('should return data that equal the deeply nested output fixture', () => {
23+
it('should return data that equal the deeply nested output fixture', async () => {
2424
const model = new ColumnsModel('', buildMockMemento())
25-
model.transformAndSet(deeplyNestedOutput)
25+
await model.transformAndSet(deeplyNestedOutput)
2626
expect(model.getSelected()).toStrictEqual(deeplyNestedColumns)
2727
})
2828
describe('persistence', () => {

extension/src/experiments/columns/model.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Memento } from 'vscode'
2-
import { collectChanges, collectColumns } from './collect'
2+
import { collectChanges, collectColumns, collectParamsFiles } from './collect'
33
import { splitColumnPath } from './paths'
44
import { Column, ColumnType } from '../webview/contract'
55
import { ExperimentsOutput } from '../../cli/reader'
@@ -10,6 +10,7 @@ export class ColumnsModel extends PathSelectionModel<Column> {
1010
private columnOrderState: string[] = []
1111
private columnWidthsState: Record<string, number> = {}
1212
private columnsChanges: string[] = []
13+
private paramsFiles = new Set<string>()
1314

1415
constructor(dvcRoot: string, workspaceState: Memento) {
1516
super(
@@ -37,6 +38,10 @@ export class ColumnsModel extends PathSelectionModel<Column> {
3738
return this.columnWidthsState
3839
}
3940

41+
public getParamsFiles() {
42+
return this.paramsFiles
43+
}
44+
4045
public transformAndSet(data: ExperimentsOutput) {
4146
return Promise.all([
4247
this.transformAndSetColumns(data),
@@ -72,12 +77,16 @@ export class ColumnsModel extends PathSelectionModel<Column> {
7277
)
7378
}
7479

75-
private transformAndSetColumns(data: ExperimentsOutput) {
76-
const columns = collectColumns(data)
80+
private async transformAndSetColumns(data: ExperimentsOutput) {
81+
const [columns, paramsFiles] = await Promise.all([
82+
collectColumns(data),
83+
collectParamsFiles(this.dvcRoot, data)
84+
])
7785

7886
this.setNewStatuses(columns)
7987

8088
this.data = columns
89+
this.paramsFiles = paramsFiles
8190
}
8291

8392
private transformAndSetChanges(data: ExperimentsOutput) {

extension/src/experiments/index.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Event, EventEmitter, Memento } from 'vscode'
1+
import { Event, EventEmitter, Memento, window } from 'vscode'
22
import { ExperimentsModel } from './model'
33
import { pickExperiments } from './model/quickPicks'
44
import { pickAndModifyParams } from './model/queue/quickPick'
@@ -34,6 +34,8 @@ import { EventName } from '../telemetry/constants'
3434
import { Toast } from '../vscode/toast'
3535
import { getInput } from '../vscode/inputBox'
3636
import { createTypedAccumulator } from '../util/object'
37+
import { setContextValue } from '../vscode/context'
38+
import { standardizePath } from '../fileSystem/path'
3739

3840
export const ExperimentsScale = {
3941
...ColumnType,
@@ -42,6 +44,7 @@ export const ExperimentsScale = {
4244
} as const
4345

4446
export class Experiments extends BaseRepository<TableData> {
47+
public readonly onDidChangeIsParamsFileFocused: Event<string | undefined>
4548
public readonly onDidChangeExperiments: Event<ExperimentsOutput | void>
4649
public readonly onDidChangeColumns: Event<void>
4750
public readonly onDidChangeCheckpoints: Event<void>
@@ -55,6 +58,10 @@ export class Experiments extends BaseRepository<TableData> {
5558
private readonly columns: ColumnsModel
5659
private readonly checkpoints: CheckpointsModel
5760

61+
private readonly paramsFileFocused = this.dispose.track(
62+
new EventEmitter<string | undefined>()
63+
)
64+
5865
private readonly experimentsChanged = this.dispose.track(
5966
new EventEmitter<ExperimentsOutput | void>()
6067
)
@@ -80,6 +87,7 @@ export class Experiments extends BaseRepository<TableData> {
8087

8188
this.internalCommands = internalCommands
8289

90+
this.onDidChangeIsParamsFileFocused = this.paramsFileFocused.event
8391
this.onDidChangeExperiments = this.experimentsChanged.event
8492
this.onDidChangeColumns = this.columnsChanged.event
8593
this.onDidChangeCheckpoints = this.checkpointsChanged.event
@@ -113,6 +121,7 @@ export class Experiments extends BaseRepository<TableData> {
113121

114122
this.handleMessageFromWebview()
115123
this.setupInitialData()
124+
this.setActiveEditorContext()
116125
}
117126

118127
public update() {
@@ -537,4 +546,44 @@ export class Experiments extends BaseRepository<TableData> {
537546

538547
return experiment?.id
539548
}
549+
550+
private setActiveEditorContext() {
551+
const setActiveEditorContext = (active: boolean) => {
552+
setContextValue('dvc.params.fileActive', active)
553+
const activeDvcRoot = active ? this.dvcRoot : undefined
554+
this.paramsFileFocused.fire(activeDvcRoot)
555+
}
556+
557+
this.dispose.track(
558+
this.onDidChangeColumns(() => {
559+
const path = standardizePath(window.activeTextEditor?.document.fileName)
560+
if (!path) {
561+
return
562+
}
563+
564+
if (!this.columns.getParamsFiles().has(path)) {
565+
return
566+
}
567+
setActiveEditorContext(true)
568+
})
569+
)
570+
571+
this.dispose.track(
572+
window.onDidChangeActiveTextEditor(event => {
573+
const path = standardizePath(event?.document.fileName)
574+
if (!path) {
575+
setActiveEditorContext(false)
576+
return
577+
}
578+
579+
if (path.includes(this.dvcRoot)) {
580+
if (this.columns.getParamsFiles().has(path)) {
581+
setActiveEditorContext(true)
582+
return
583+
}
584+
setActiveEditorContext(false)
585+
}
586+
})
587+
)
588+
}
540589
}

extension/src/experiments/workspace.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ export class WorkspaceExperiments extends BaseWorkspaceWebviews<
2424

2525
private readonly checkpointsChanged: EventEmitter<void>
2626

27+
private focusedParamsDvcRoot: string | undefined
28+
2729
constructor(
2830
internalCommands: InternalCommands,
2931
updatesPaused: EventEmitter<boolean>,
@@ -232,6 +234,13 @@ export class WorkspaceExperiments extends BaseWorkspaceWebviews<
232234
dvcRoot => (this.focusedWebviewDvcRoot = dvcRoot)
233235
)
234236
)
237+
238+
experiments.dispose.track(
239+
experiments.onDidChangeIsParamsFileFocused(
240+
dvcRoot => (this.focusedParamsDvcRoot = dvcRoot)
241+
)
242+
)
243+
235244
experiments.dispose.track(
236245
experiments.onDidChangeExperiments(() => {
237246
this.experimentsChanged.fire()
@@ -253,6 +262,14 @@ export class WorkspaceExperiments extends BaseWorkspaceWebviews<
253262
return experiments
254263
}
255264

265+
public getFocusedOrOnlyOrPickProject() {
266+
return (
267+
this.focusedWebviewDvcRoot ||
268+
this.focusedParamsDvcRoot ||
269+
this.getOnlyOrPickProject()
270+
)
271+
}
272+
256273
private async pickExpThenRun(
257274
commandId: CommandId,
258275
pickFunc: (

extension/src/fileSystem/path.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { Uri } from 'vscode'
2+
3+
export const standardizePath = (path?: string): string | undefined => {
4+
if (!path) {
5+
return
6+
}
7+
return Uri.file(path).fsPath
8+
}

extension/src/persistence/model.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { PersistenceKey } from './constants'
33
import { DeferredDisposable } from '../class/deferred'
44

55
export class ModelWithPersistence extends DeferredDisposable {
6-
private readonly dvcRoot: string
6+
protected readonly dvcRoot: string
77
private readonly workspaceState: Memento
88

99
constructor(dvcRoot: string, workspaceState: Memento) {

extension/src/plots/workspace.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,8 @@ export class WorkspacePlots extends BaseWorkspaceWebviews<Plots, PlotsData> {
4646
}
4747
return this.getRepository(dvcRoot).selectPlots()
4848
}
49+
50+
public getFocusedOrOnlyOrPickProject() {
51+
return this.focusedWebviewDvcRoot || this.getOnlyOrPickProject()
52+
}
4953
}

0 commit comments

Comments
 (0)