Skip to content

Commit c6226b9

Browse files
authored
Merge pull request wso2#1443 from xlight05/review-improve
Add new/old preview in Copilot review mode
2 parents ea6fa20 + fd13318 commit c6226b9

8 files changed

Lines changed: 191 additions & 23 deletions

File tree

workspaces/ballerina/ballerina-core/src/interfaces/extended-lang-client.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,7 @@ export interface BIFlowModelRequest {
836836
startLine?: LinePosition;
837837
endLine?: LinePosition;
838838
forceAssign?: boolean;
839+
useFileSchema?: boolean;
839840
}
840841

841842
export interface BISuggestedFlowModelRequest extends BIFlowModelRequest {
@@ -969,6 +970,7 @@ export type BIGetEnclosedFunctionRequest = {
969970
filePath: string;
970971
position: LinePosition;
971972
findClass?: boolean;
973+
useFileSchema?: boolean;
972974
}
973975

974976
export type BIGetEnclosedFunctionResponse = {
@@ -1059,6 +1061,7 @@ export interface BICopilotContextResponse {
10591061

10601062
export interface BIDesignModelRequest {
10611063
projectPath?: string;
1064+
useFileSchema?: boolean;
10621065
}
10631066

10641067
export type BIDesignModelResponse = {
@@ -1509,6 +1512,7 @@ export interface GetGraphqlTypeResponse {
15091512

15101513
export interface GetTypesRequest {
15111514
filePath: string;
1515+
useFileSchema?: boolean;
15121516
}
15131517

15141518
export interface GetTypeRequest {

workspaces/ballerina/ballerina-core/src/rpc-types/ai-panel/interfaces.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -384,8 +384,9 @@ export interface SemanticDiffRequest {
384384
// Numeric enum values from the API
385385
export enum ChangeTypeEnum {
386386
ADDITION = 0,
387-
MODIFICATION = 1,
388-
DELETION = 2
387+
DELETION = 1,
388+
MODIFICATION = 2,
389+
389390
}
390391

391392
export type ChangeType = "ADDITION" | "MODIFICATION" | "DELETION";

workspaces/ballerina/ballerina-extension/src/rpc-managers/bi-diagram/rpc-manager.ts

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,31 @@ import { getCurrentBallerinaProject } from "../../utils/project-utils";
181181
export class BiDiagramRpcManager implements BIDiagramAPI {
182182
OpenConfigTomlRequest: (params: OpenConfigTomlRequest) => Promise<void>;
183183

184+
private toRawPath(input: string): string {
185+
if (input.includes('://')) {
186+
return Uri.parse(input).fsPath;
187+
}
188+
return input;
189+
}
190+
191+
private mapTempPathToOriginal(tempFilePath: string): string {
192+
const rawPath = this.toRawPath(tempFilePath);
193+
const context = StateMachine.context();
194+
const originalRoot = context.workspacePath || context.projectPath;
195+
const workspaceId = context.projectPath;
196+
const threadId = 'default';
197+
const pendingReview = chatStateStorage.getPendingReviewGeneration(workspaceId, threadId);
198+
if (pendingReview?.reviewState?.tempProjectPath && originalRoot) {
199+
const normalizedTempRoot = pendingReview.reviewState.tempProjectPath.replace(/\\/g, '/');
200+
const normalizedFilePath = rawPath.replace(/\\/g, '/');
201+
if (normalizedFilePath.startsWith(normalizedTempRoot)) {
202+
const relativePath = normalizedFilePath.substring(normalizedTempRoot.length);
203+
return originalRoot + relativePath;
204+
}
205+
}
206+
return rawPath;
207+
}
208+
184209
async getFlowModel(params: BIFlowModelRequest): Promise<BIFlowModelResponse> {
185210
console.log(">>> requesting bi flow model from ls", params);
186211
return new Promise((resolve) => {
@@ -189,8 +214,13 @@ export class BiDiagramRpcManager implements BIDiagramAPI {
189214
// If params has all required fields, use them directly
190215
if (params?.filePath && params?.startLine && params?.endLine) {
191216
console.log(">>> using params to create request");
217+
let filePath = params.filePath;
218+
// When useFileSchema is set, map temp path to original project path
219+
if (params.useFileSchema) {
220+
filePath = this.mapTempPathToOriginal(filePath);
221+
}
192222
request = {
193-
filePath: params.filePath,
223+
filePath,
194224
startLine: params.startLine,
195225
endLine: params.endLine,
196226
forceAssign: params.forceAssign ?? true,
@@ -199,7 +229,7 @@ export class BiDiagramRpcManager implements BIDiagramAPI {
199229
// Fall back to context if params are not complete
200230
console.log(">>> params incomplete, falling back to context");
201231
const context = StateMachine.context();
202-
232+
203233
if (!context.position) {
204234
// TODO: check why this hits when we are in review mode
205235
console.log(">>> position not found in context, cannot create request");
@@ -1438,9 +1468,15 @@ export class BiDiagramRpcManager implements BIDiagramAPI {
14381468

14391469
async getEnclosedFunction(params: BIGetEnclosedFunctionRequest): Promise<BIGetEnclosedFunctionResponse> {
14401470
console.log(">>> requesting parent functin definition", params);
1471+
// When useFileSchema is set, map temp path to original project path
1472+
let filePath = params.filePath;
1473+
if (params.useFileSchema) {
1474+
filePath = this.mapTempPathToOriginal(filePath);
1475+
}
1476+
const request = { filePath, position: params.position, findClass: params.findClass };
14411477
return new Promise((resolve) => {
14421478
StateMachine.langClient()
1443-
.getEnclosedFunctionDef(params)
1479+
.getEnclosedFunctionDef(request)
14441480
.then((response) => {
14451481
if (response?.filePath && response?.startLine && response?.endLine) {
14461482
console.log(">>> parent function position ", response);
@@ -1514,8 +1550,13 @@ export class BiDiagramRpcManager implements BIDiagramAPI {
15141550
return new Promise((resolve) => {
15151551
let projectPath: string;
15161552
if (params?.projectPath) {
1517-
const uri = Uri.file(params.projectPath);
1518-
projectPath = uri.with({ scheme: 'ai' }).toString();
1553+
if (params.useFileSchema) {
1554+
// Map temp project path to original project raw path
1555+
projectPath = this.mapTempPathToOriginal(params.projectPath);
1556+
} else {
1557+
const uri = Uri.file(params.projectPath);
1558+
projectPath = uri.with({ scheme: 'ai' }).toString();
1559+
}
15191560
} else {
15201561
projectPath = StateMachine.context().projectPath;
15211562
}
@@ -1552,6 +1593,11 @@ export class BiDiagramRpcManager implements BIDiagramAPI {
15521593
});
15531594
}
15541595

1596+
// When useFileSchema is set, map temp path to original project path
1597+
if (params.useFileSchema) {
1598+
filePath = this.mapTempPathToOriginal(filePath);
1599+
}
1600+
15551601
return new Promise((resolve, reject) => {
15561602
StateMachine.langClient()
15571603
.getTypes({ filePath })

workspaces/ballerina/ballerina-visualizer/src/views/ReviewMode/ReadonlyComponentDiagram.tsx

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,28 +39,50 @@ interface ReadonlyComponentDiagramProps {
3939
projectPath: string;
4040
filePath: string;
4141
position: NodePosition;
42+
useFileSchema?: boolean;
43+
}
44+
45+
const EmptyMessage = styled.div`
46+
display: flex;
47+
justify-content: center;
48+
align-items: center;
49+
height: 100%;
50+
color: var(--vscode-descriptionForeground);
51+
font-size: 14px;
52+
`;
53+
54+
function isDesignModelEmpty(model: CDModel): boolean {
55+
return model.connections.length === 0
56+
&& model.listeners.length === 0
57+
&& model.services.length === 0
58+
&& !model.automation;
4259
}
4360

4461
export function ReadonlyComponentDiagram(props: ReadonlyComponentDiagramProps): JSX.Element {
45-
const { projectPath } = props;
62+
const { projectPath, useFileSchema } = props;
4663
const { rpcClient } = useRpcContext();
4764
const [project, setProject] = useState<CDModel | null>(null);
65+
const [isLoaded, setIsLoaded] = useState(false);
4866

4967
useEffect(() => {
68+
setProject(null);
69+
setIsLoaded(false);
5070
fetchProject();
51-
}, [projectPath]);
71+
}, [projectPath, useFileSchema]);
5272

5373
const fetchProject = () => {
5474
rpcClient
5575
.getBIDiagramRpcClient()
56-
.getDesignModel({ projectPath })
76+
.getDesignModel({ projectPath, useFileSchema })
5777
.then((response) => {
5878
if (response?.designModel) {
5979
setProject(response.designModel);
6080
}
81+
setIsLoaded(true);
6182
})
6283
.catch((error) => {
6384
console.error("Error getting design model", error);
85+
setIsLoaded(true);
6486
});
6587
};
6688

@@ -69,14 +91,22 @@ export function ReadonlyComponentDiagram(props: ReadonlyComponentDiagramProps):
6991
console.log("Diagram is in readonly mode");
7092
};
7193

72-
if (!project) {
94+
if (!isLoaded) {
7395
return (
7496
<SpinnerContainer>
7597
<ProgressRing color={ThemeColors.PRIMARY} />
7698
</SpinnerContainer>
7799
);
78100
}
79101

102+
if (!project || isDesignModelEmpty(project)) {
103+
return (
104+
<EmptyMessage>
105+
No top-level constructs found in the previous version
106+
</EmptyMessage>
107+
);
108+
}
109+
80110
return (
81111
<Container>
82112
<Diagram

workspaces/ballerina/ballerina-visualizer/src/views/ReviewMode/ReadonlyFlowDiagram.tsx

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,35 +46,52 @@ interface ReadonlyFlowDiagramProps {
4646
filePath: string;
4747
position: NodePosition;
4848
onModelLoaded?: (metadata: ItemMetadata) => void;
49+
useFileSchema?: boolean;
4950
}
5051

5152
export function ReadonlyFlowDiagram(props: ReadonlyFlowDiagramProps): JSX.Element {
52-
const { filePath, position, onModelLoaded } = props;
53+
const { filePath, position, onModelLoaded, useFileSchema } = props;
5354
const { rpcClient } = useRpcContext();
5455
const [flowModel, setFlowModel] = useState<Flow | null>(null);
5556

5657
useEffect(() => {
58+
setFlowModel(null);
5759
fetchFlowModel();
58-
}, [filePath, position]);
60+
}, [filePath, position, useFileSchema]);
5961

6062
const fetchFlowModel = () => {
63+
// First resolve the full function range using getEnclosedFunction,
64+
// since the position from semantic diff may only cover the changed statement
6165
rpcClient
6266
.getBIDiagramRpcClient()
63-
.getFlowModel({
67+
.getEnclosedFunction({
6468
filePath: filePath,
65-
startLine: { line: position.startLine, offset: position.startColumn },
66-
endLine: { line: position.endLine, offset: position.endColumn },
69+
position: { line: position.startLine, offset: position.startColumn },
70+
useFileSchema,
71+
})
72+
.then((enclosedFn) => {
73+
const startLine = enclosedFn?.startLine ?? { line: position.startLine, offset: position.startColumn };
74+
const endLine = enclosedFn?.endLine ?? { line: position.endLine, offset: position.endColumn };
75+
76+
return rpcClient
77+
.getBIDiagramRpcClient()
78+
.getFlowModel({
79+
filePath: filePath,
80+
startLine,
81+
endLine,
82+
useFileSchema,
83+
});
6784
})
6885
.then((response) => {
6986
if (response?.flowModel) {
7087
setFlowModel(response.flowModel);
71-
88+
7289
// Extract metadata from EVENT_START node
7390
if (onModelLoaded && response.flowModel.nodes) {
7491
const eventStartNode = response.flowModel.nodes.find(
7592
(node: any) => node.codedata?.node === 'EVENT_START'
7693
);
77-
94+
7895
if (eventStartNode?.metadata?.data) {
7996
const data = eventStartNode.metadata.data as any;
8097
onModelLoaded({

workspaces/ballerina/ballerina-visualizer/src/views/ReviewMode/ReadonlyTypeDiagram.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,21 +45,23 @@ interface ReadonlyTypeDiagramProps {
4545
projectPath: string;
4646
filePath: string;
4747
onModelLoaded?: (metadata: ItemMetadata) => void;
48+
useFileSchema?: boolean;
4849
}
4950

5051
export function ReadonlyTypeDiagram(props: ReadonlyTypeDiagramProps): JSX.Element {
51-
const { filePath, onModelLoaded } = props;
52+
const { filePath, onModelLoaded, useFileSchema } = props;
5253
const { rpcClient } = useRpcContext();
5354
const [typesModel, setTypesModel] = useState<Type[] | null>(null);
5455

5556
useEffect(() => {
57+
setTypesModel(null);
5658
fetchTypesModel();
57-
}, [filePath]);
59+
}, [filePath, useFileSchema]);
5860

5961
const fetchTypesModel = () => {
6062
rpcClient
6163
.getBIDiagramRpcClient()
62-
.getTypes({ filePath: filePath })
64+
.getTypes({ filePath: filePath, useFileSchema })
6365
.then((response) => {
6466
if (response?.types) {
6567
setTypesModel(response.types);

workspaces/ballerina/ballerina-visualizer/src/views/ReviewMode/ReviewNavigation.tsx

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,34 @@ const NavigationButtons = styled.div`
8080
gap: 8px;
8181
`;
8282

83+
const VersionToggle = styled.div`
84+
display: flex;
85+
border: 1px solid var(--vscode-panel-border);
86+
border-radius: 4px;
87+
overflow: hidden;
88+
`;
89+
90+
const ToggleSegment = styled.button<{ active: boolean; disabled?: boolean }>`
91+
background: ${(props: { active: boolean }) =>
92+
props.active ? "var(--vscode-button-background)" : "transparent"};
93+
color: ${(props: { active: boolean }) =>
94+
props.active ? "var(--vscode-button-foreground)" : "var(--vscode-foreground)"};
95+
border: none;
96+
padding: 4px 12px;
97+
font-size: 12px;
98+
font-family: var(--vscode-font-family);
99+
cursor: ${(props: { disabled?: boolean }) => (props.disabled ? "default" : "pointer")};
100+
opacity: ${(props: { disabled?: boolean }) => (props.disabled ? 0.5 : 1)};
101+
transition: background 0.15s, color 0.15s;
102+
103+
&:hover:not(:disabled) {
104+
background: ${(props: { active: boolean }) =>
105+
props.active
106+
? "var(--vscode-button-hoverBackground)"
107+
: "var(--vscode-toolbar-hoverBackground)"};
108+
}
109+
`;
110+
83111
const ActionButtons = styled.div`
84112
display: flex;
85113
gap: 8px;
@@ -98,6 +126,9 @@ interface ReviewNavigationProps {
98126
onReject: () => void;
99127
canGoPrevious: boolean;
100128
canGoNext: boolean;
129+
showOldVersion: boolean;
130+
onToggleVersion: () => void;
131+
canToggleVersion: boolean;
101132
}
102133

103134
export function ReviewNavigation(props: ReviewNavigationProps): JSX.Element {
@@ -110,7 +141,10 @@ export function ReviewNavigation(props: ReviewNavigationProps): JSX.Element {
110141
onAccept,
111142
onReject,
112143
canGoPrevious,
113-
canGoNext
144+
canGoNext,
145+
showOldVersion,
146+
onToggleVersion,
147+
canToggleVersion
114148
} = props;
115149

116150
const [isProcessing, setIsProcessing] = useState(false);
@@ -177,6 +211,25 @@ export function ReviewNavigation(props: ReviewNavigationProps): JSX.Element {
177211
{currentLabel && <ViewLabel title={currentLabel}>{currentLabel}</ViewLabel>}
178212
</ViewInfo>
179213

214+
<VersionToggle>
215+
<ToggleSegment
216+
active={!showOldVersion}
217+
disabled={!canToggleVersion}
218+
onClick={() => { if (canToggleVersion && showOldVersion) { onToggleVersion(); } }}
219+
title="Show new version"
220+
>
221+
New
222+
</ToggleSegment>
223+
<ToggleSegment
224+
active={showOldVersion}
225+
disabled={!canToggleVersion}
226+
onClick={() => { if (canToggleVersion && !showOldVersion) { onToggleVersion(); } }}
227+
title="Show old version"
228+
>
229+
Old
230+
</ToggleSegment>
231+
</VersionToggle>
232+
180233
<ActionButtons>
181234
<Button
182235
appearance="secondary"

0 commit comments

Comments
 (0)