Skip to content

Commit bbcdef3

Browse files
authored
Make schema editor field row clickable (#2247)
1 parent f795639 commit bbcdef3

File tree

7 files changed

+203
-34
lines changed

7 files changed

+203
-34
lines changed

packages/host/app/components/operator-mode/card-adoption-chain.gts

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Component from '@glimmer/component';
33

44
import { LoadingIndicator } from '@cardstack/boxel-ui/components';
55

6-
import { type ResolvedCodeRef } from '@cardstack/runtime-common/code-ref';
6+
import { type CodeRef } from '@cardstack/runtime-common/code-ref';
77
import { ModuleSyntax } from '@cardstack/runtime-common/module-syntax';
88

99
import CardSchemaEditor from '@cardstack/host/components/operator-mode/card-schema-editor';
@@ -24,8 +24,9 @@ interface Signature {
2424
moduleSyntax: ModuleSyntax;
2525
isReadOnly: boolean;
2626
goToDefinition: (
27-
codeRef: ResolvedCodeRef | undefined,
27+
codeRef: CodeRef | undefined,
2828
localName: string | undefined,
29+
fieldName?: string,
2930
) => void;
3031
isLoading: boolean;
3132
};

packages/host/app/components/operator-mode/card-schema-editor.gts

+55-6
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { ArrowTopLeft, IconLink, IconPlus } from '@cardstack/boxel-ui/icons';
2020

2121
import { getPlural } from '@cardstack/runtime-common';
2222

23-
import { type ResolvedCodeRef } from '@cardstack/runtime-common/code-ref';
23+
import { type CodeRef } from '@cardstack/runtime-common/code-ref';
2424
import type { ModuleSyntax } from '@cardstack/runtime-common/module-syntax';
2525

2626
import EditFieldModal from '@cardstack/host/components/operator-mode/edit-field-modal';
@@ -57,8 +57,9 @@ interface Signature {
5757
childFields: string[];
5858
parentFields: string[];
5959
goToDefinition: (
60-
codeRef: ResolvedCodeRef | undefined,
60+
codeRef: CodeRef | undefined,
6161
localName: string | undefined,
62+
fieldName?: string,
6263
) => void;
6364
};
6465
}
@@ -91,6 +92,8 @@ export default class CardSchemaEditor extends Component<Signature> {
9192
border-radius: var(--code-mode-container-border-radius);
9293
background-color: var(--boxel-light);
9394
overflow: hidden;
95+
cursor: pointer;
96+
width: 100%;
9497
}
9598
.card-field + .card-field {
9699
margin-top: var(--boxel-sp-xxs);
@@ -163,6 +166,13 @@ export default class CardSchemaEditor extends Component<Signature> {
163166
overflow: hidden;
164167
text-overflow: ellipsis;
165168
white-space: nowrap;
169+
border: none;
170+
background-color: transparent;
171+
padding: 0;
172+
text-align: left;
173+
}
174+
.field-name:hover {
175+
color: var(--boxel-highlight);
166176
}
167177
168178
.overridden-field {
@@ -244,7 +254,10 @@ export default class CardSchemaEditor extends Component<Signature> {
244254
<Pill
245255
class='field-pill'
246256
@kind='button'
247-
{{on 'click' (fn @goToDefinition codeRef @cardType.localName)}}
257+
{{on
258+
'click'
259+
(fn @goToDefinition codeRef @cardType.localName undefined)
260+
}}
248261
data-test-card-schema-navigational-button
249262
>
250263
<:iconLeft>
@@ -293,15 +306,17 @@ export default class CardSchemaEditor extends Component<Signature> {
293306
data-test-field-name={{field.name}}
294307
>
295308
<div class='left'>
296-
<div
309+
<button
297310
class={{if
298311
(this.isOverridden field)
299312
'field-name overridden-field'
300313
'field-name'
301314
}}
315+
data-test-field-name-button={{field.name}}
316+
{{on 'click' (fn this.goToField field)}}
302317
>
303318
{{field.name}}
304-
</div>
319+
</button>
305320
<div class='field-types' data-test-field-types>
306321
{{this.fieldTypes field}}
307322
</div>
@@ -333,7 +348,12 @@ export default class CardSchemaEditor extends Component<Signature> {
333348
@kind='button'
334349
{{on
335350
'click'
336-
(fn @goToDefinition codeRef field.card.localName)
351+
(fn
352+
@goToDefinition
353+
codeRef
354+
field.card.localName
355+
undefined
356+
)
337357
}}
338358
data-test-card-schema-field-navigational-button
339359
>
@@ -544,4 +564,33 @@ export default class CardSchemaEditor extends Component<Signature> {
544564
inline: 'nearest',
545565
});
546566
}
567+
568+
@action
569+
private goToField(field: FieldOfType) {
570+
if (
571+
`${this.args.cardType.module}.gts` ===
572+
this.operatorModeStateService.codePathString
573+
) {
574+
// In the same file
575+
this.args.goToDefinition(
576+
undefined,
577+
this.args.cardType.localName,
578+
field.name,
579+
);
580+
return;
581+
}
582+
583+
let codeRef = getCodeRef(this.args.cardType);
584+
if (!codeRef) {
585+
return undefined;
586+
}
587+
this.args.goToDefinition(
588+
{
589+
type: 'fieldOf',
590+
card: codeRef,
591+
field: field.name,
592+
},
593+
this.args.cardType.localName,
594+
);
595+
}
547596
}

packages/host/app/components/operator-mode/code-editor.gts

+54-8
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ interface Signature {
4545
saveSourceOnClose: (url: URL, content: string) => void;
4646
selectDeclaration: (declaration: ModuleDeclaration) => void;
4747
onFileSave: (status: 'started' | 'finished') => void;
48-
onSetup: (updateCursorByName: (name: string) => void) => void;
48+
onSetup: (
49+
updateCursorByName: (name: string, fieldName?: string) => void,
50+
) => void;
4951
};
5052
}
5153

@@ -137,12 +139,36 @@ export default class CodeEditor extends Component<Signature> {
137139
}
138140
}
139141

142+
let selectedFieldName = this.operatorModeStateService.state.fieldSelection;
143+
let { selectedDeclaration } = this.args;
144+
if (
145+
selectedFieldName &&
146+
selectedDeclaration &&
147+
'possibleFields' in selectedDeclaration &&
148+
selectedDeclaration.possibleFields
149+
) {
150+
let possibleFields = selectedDeclaration.possibleFields;
151+
let field = possibleFields.get(selectedFieldName);
152+
let loc =
153+
field?.path?.node && 'loc' in field.path.node && field.path.node.loc
154+
? field.path.node.loc
155+
: undefined;
156+
if (loc) {
157+
let { start } = loc;
158+
let { line, column } = start;
159+
// Adjusts column to make cursor position right after the field name
160+
let fieldDecoratorTextLength = 8;
161+
column = column + fieldDecoratorTextLength + selectedFieldName.length;
162+
return new Position(line, column);
163+
}
164+
}
165+
140166
let loc =
141-
this.args.selectedDeclaration?.path?.node &&
142-
'body' in this.args.selectedDeclaration.path.node &&
143-
'loc' in this.args.selectedDeclaration.path.node.body &&
144-
this.args.selectedDeclaration.path.node.body.loc
145-
? this.args.selectedDeclaration?.path?.node.body.loc
167+
selectedDeclaration?.path?.node &&
168+
'body' in selectedDeclaration.path.node &&
169+
'loc' in selectedDeclaration.path.node.body &&
170+
selectedDeclaration.path.node.body.loc
171+
? selectedDeclaration?.path?.node.body.loc
146172
: undefined;
147173
if (loc) {
148174
let { start } = loc;
@@ -152,17 +178,37 @@ export default class CodeEditor extends Component<Signature> {
152178
}
153179

154180
@action
155-
private updateMonacoCursorPositionByName(name: string) {
181+
private updateMonacoCursorPositionByName(name: string, fieldName?: string) {
156182
let declaration = findDeclarationByName(name, this.declarations);
157183
if (declaration === undefined) return;
158-
return this.updateMonacoCursorPositionByDeclaration(declaration);
184+
return this.updateMonacoCursorPositionByDeclaration(declaration, fieldName);
159185
}
160186

161187
@action
162188
private updateMonacoCursorPositionByDeclaration(
163189
declaration: ModuleDeclaration,
190+
fieldName?: string,
164191
) {
165192
if (
193+
fieldName &&
194+
'possibleFields' in declaration &&
195+
declaration.possibleFields
196+
) {
197+
let possibleFields = declaration.possibleFields;
198+
let field = possibleFields.get(fieldName);
199+
let loc =
200+
field?.path?.node && 'loc' in field.path.node && field.path.node.loc
201+
? field.path.node.loc
202+
: undefined;
203+
if (loc) {
204+
// Adjusts column to make cursor position right after the field name
205+
let fieldDecoratorTextLength = 8;
206+
let columnAdjustment = fieldDecoratorTextLength + fieldName.length;
207+
this.monacoService.updateCursorPosition(
208+
new Position(loc.start.line, loc.start.column + columnAdjustment),
209+
);
210+
}
211+
} else if (
166212
declaration.path?.node &&
167213
'body' in declaration.path.node &&
168214
'loc' in declaration.path.node.body &&

packages/host/app/components/operator-mode/code-submode.gts

+16-8
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
isResolvedCodeRef,
3737
type ResolvedCodeRef,
3838
PermissionsContextName,
39+
CodeRef,
3940
} from '@cardstack/runtime-common';
4041
import { isEquivalentBodyPosition } from '@cardstack/runtime-common/schema-analysis-plugin';
4142

@@ -158,7 +159,9 @@ export default class CodeSubmode extends Component<Signature> {
158159

159160
private defaultPanelWidths: PanelWidths;
160161
private defaultPanelHeights: PanelHeights;
161-
private updateCursorByName: ((name: string) => void) | undefined;
162+
private updateCursorByName:
163+
| ((name: string, fieldName?: string) => void)
164+
| undefined;
162165
private panelSelections: Record<string, SelectedAccordionItem>;
163166

164167
private createFileModal: CreateFileModal | undefined;
@@ -497,10 +500,11 @@ export default class CodeSubmode extends Component<Signature> {
497500

498501
@action
499502
private goToDefinitionAndResetCursorPosition(
500-
codeRef: ResolvedCodeRef | undefined,
503+
codeRef: CodeRef | undefined,
501504
localName: string | undefined,
505+
fieldName?: string,
502506
) {
503-
this.goToDefinition(codeRef, localName);
507+
this.goToDefinition(codeRef, localName, fieldName);
504508
if (this.codePath) {
505509
let urlString = this.codePath.toString();
506510
this.recentFilesService.updateCursorPositionByURL(
@@ -512,14 +516,16 @@ export default class CodeSubmode extends Component<Signature> {
512516

513517
@action
514518
private goToDefinition(
515-
codeRef: ResolvedCodeRef | undefined,
519+
codeRef: CodeRef | undefined,
516520
localName: string | undefined,
521+
fieldName?: string,
517522
) {
518-
this.operatorModeStateService.updateCodePathWithCodeSelection(
523+
this.operatorModeStateService.updateCodePathWithSelection({
519524
codeRef,
520525
localName,
521-
this.updateCursorByName,
522-
);
526+
fieldName,
527+
onLocalSelection: this.updateCursorByName,
528+
});
523529
}
524530

525531
private loadScopedCSS = restartableTask(async () => {
@@ -713,7 +719,9 @@ export default class CodeSubmode extends Component<Signature> {
713719
this.createFileModal = createFileModal;
714720
};
715721

716-
private setupCodeEditor = (updateCursorByName: (name: string) => void) => {
722+
private setupCodeEditor = (
723+
updateCursorByName: (name: string, fieldName?: string) => void,
724+
) => {
717725
this.updateCursorByName = updateCursorByName;
718726
};
719727

packages/host/app/components/operator-mode/code-submode/schema-editor.gts

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import Component from '@glimmer/component';
55
import { cached } from '@glimmer/tracking';
66

77
import { getPlural } from '@cardstack/runtime-common';
8-
import { type ResolvedCodeRef } from '@cardstack/runtime-common/code-ref';
8+
import { type CodeRef } from '@cardstack/runtime-common/code-ref';
99

1010
import { ModuleSyntax } from '@cardstack/runtime-common/module-syntax';
1111

@@ -30,8 +30,9 @@ interface Signature {
3030
card: typeof BaseDef;
3131
isReadOnly: boolean;
3232
goToDefinition: (
33-
codeRef: ResolvedCodeRef | undefined,
33+
codeRef: CodeRef | undefined,
3434
localName: string | undefined,
35+
fieldName?: string,
3536
) => void;
3637
};
3738
Blocks: {

0 commit comments

Comments
 (0)