Skip to content

Commit d339dce

Browse files
committed
Refactor MarkdownToolbar and CodeUtils for improved token processing
1 parent eb02bf8 commit d339dce

File tree

3 files changed

+111
-124
lines changed

3 files changed

+111
-124
lines changed

workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/controls/MarkdownToolbar.tsx

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import React from "react";
2020
import styled from "@emotion/styled";
2121
import { ThemeColors, Icon, Switch } from "@wso2/ui-toolkit";
2222
import "@github/markdown-toolbar-element";
23-
import { HelperPaneToggleButton } from "../../../editors/MultiModeExpressionEditor/ChipExpressionEditor/components/HelperPaneToggleButton";
2423

2524
// Type declarations for GitHub markdown toolbar custom elements
2625
declare global {
@@ -108,12 +107,6 @@ interface MarkdownToolbarProps {
108107
isPreviewMode?: boolean;
109108
/** Callback to toggle preview mode */
110109
onTogglePreview?: () => void;
111-
/** Helper pane toggle button props (optional) */
112-
helperPaneToggle?: {
113-
ref: React.RefObject<HTMLButtonElement>;
114-
isOpen: boolean;
115-
onClick: () => void;
116-
};
117110
}
118111

119112
/**
@@ -123,8 +116,7 @@ interface MarkdownToolbarProps {
123116
export const MarkdownToolbar: React.FC<MarkdownToolbarProps> = ({
124117
textareaId,
125118
isPreviewMode = false,
126-
onTogglePreview,
127-
helperPaneToggle
119+
onTogglePreview
128120
}) => {
129121
return (
130122
<ToolbarContainer>

workspaces/ballerina/ballerina-side-panel/src/components/editors/ExpandedEditor/utils/transformToMarkdown.ts

Lines changed: 31 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
* under the License.
1717
*/
1818

19+
import { iterateTokenStream } from "../../MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils";
1920
import { TokenType } from "../../MultiModeExpressionEditor/ChipExpressionEditor/types";
2021
import { getParsedExpressionTokens, detectTokenPatterns } from "../../MultiModeExpressionEditor/ChipExpressionEditor/utils";
2122

@@ -50,96 +51,63 @@ export const transformExpressionToMarkdown = (
5051
}
5152

5253
try {
53-
// Parse tokens to get absolute positions
5454
const parsedTokens = getParsedExpressionTokens(tokenStream, expression);
55-
56-
// Detect compound token patterns (variables and documents)
5755
const compounds = detectTokenPatterns(parsedTokens, expression);
58-
5956
const renderableTokens: RenderableToken[] = [];
6057

61-
// Create a set of token indices that are part of compounds
62-
const compoundTokenIndices = new Set<number>();
63-
for (const compound of compounds) {
64-
for (let i = compound.startIndex; i <= compound.endIndex; i++) {
65-
compoundTokenIndices.add(i);
66-
}
67-
}
68-
69-
// Process compound tokens
70-
for (const compound of compounds) {
71-
let chipTag: string;
72-
73-
if (compound.tokenType === TokenType.DOCUMENT && compound.metadata.documentType) {
74-
chipTag = createChipTag('document', compound.metadata.content, compound.metadata.documentType);
75-
} else if (compound.tokenType === TokenType.VARIABLE) {
76-
chipTag = createChipTag('variable', compound.metadata.content);
77-
} else {
78-
// Skip unknown token types
79-
continue;
80-
}
81-
82-
renderableTokens.push({
83-
start: compound.start,
84-
end: compound.end,
85-
chipTag
86-
});
87-
}
88-
89-
// Process individual tokens that are not part of compounds
90-
for (let i = 0; i < parsedTokens.length; i++) {
91-
const token = parsedTokens[i];
92-
93-
// Skip tokens that are part of compound sequences
94-
if (compoundTokenIndices.has(i)) {
95-
continue;
58+
// Use the shared iterator from CodeUtils
59+
iterateTokenStream(parsedTokens, compounds, expression, {
60+
onCompound: (compound) => {
61+
let chipTag: string | undefined;
62+
if (compound.tokenType === TokenType.DOCUMENT && compound.metadata.documentType) {
63+
chipTag = createChipTag(TokenType.DOCUMENT, compound.metadata.content, compound.metadata.documentType);
64+
} else if (compound.tokenType === TokenType.VARIABLE) {
65+
chipTag = createChipTag(TokenType.VARIABLE, compound.metadata.content);
66+
}
67+
68+
if (chipTag) {
69+
renderableTokens.push({
70+
start: compound.start,
71+
end: compound.end,
72+
chipTag
73+
});
74+
}
75+
},
76+
onToken: (token, content) => {
77+
const chipTag = createChipTag(token.type, content);
78+
if (chipTag) {
79+
renderableTokens.push({
80+
start: token.start,
81+
end: token.end,
82+
chipTag
83+
});
84+
}
9685
}
97-
98-
// Skip START_EVENT and END_EVENT tokens
99-
if (token.type === TokenType.START_EVENT || token.type === TokenType.END_EVENT) {
100-
continue;
101-
}
102-
103-
// Get the actual text content for this token
104-
const content = expression.slice(token.start, token.end);
105-
106-
let chipTag = createChipTag(token.type, content);
107-
108-
if (chipTag) {
109-
renderableTokens.push({
110-
start: token.start,
111-
end: token.end,
112-
chipTag
113-
});
114-
}
115-
}
86+
});
11687

11788
// If no tokens to render, return original expression
11889
if (renderableTokens.length === 0) {
11990
return expression;
12091
}
12192

12293
// Sort by position in reverse order to maintain position integrity
123-
// when replacing (replace from end to start)
12494
const sortedTokens = renderableTokens.sort((a, b) => b.start - a.start);
12595

12696
let transformed = expression;
127-
12897
for (const token of sortedTokens) {
129-
// Replace the token range with chip tag
13098
transformed =
13199
transformed.slice(0, token.start) +
132100
token.chipTag +
133101
transformed.slice(token.end);
134102
}
135103
return transformed;
104+
136105
} catch (error) {
137106
console.error('Error transforming expression to markdown:', error);
138-
// Return original expression if transformation fails
139107
return expression;
140108
}
141109
};
142110

143111
export const hasTokens = (expression: string): boolean => {
144112
return /\$\{[^}]+\}/.test(expression);
145-
};
113+
};

workspaces/ballerina/ballerina-side-panel/src/components/editors/MultiModeExpressionEditor/ChipExpressionEditor/CodeUtils.ts

Lines changed: 79 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,58 @@ export const tokenField = StateField.define<TokenFieldState>({
258258
}
259259
});
260260

261+
export const iterateTokenStream = (
262+
tokens: ParsedToken[],
263+
compounds: CompoundTokenSequence[],
264+
content: string,
265+
callbacks: {
266+
onCompound: (compound: CompoundTokenSequence) => void;
267+
onToken: (token: ParsedToken, text: string) => void;
268+
}
269+
) => {
270+
const docLength = content.length;
271+
272+
// Create a set of token indices that are part of compounds
273+
const compoundTokenIndices = new Set<number>();
274+
for (const compound of compounds) {
275+
for (let i = compound.startIndex; i <= compound.endIndex; i++) {
276+
compoundTokenIndices.add(i);
277+
}
278+
}
279+
280+
// Process compound tokens
281+
for (const compound of compounds) {
282+
// Validate compound range
283+
if (compound.start < 0 || compound.end > docLength || compound.start >= compound.end) {
284+
continue;
285+
}
286+
callbacks.onCompound(compound);
287+
}
288+
289+
// Process individual tokens that are not part of compounds
290+
for (let i = 0; i < tokens.length; i++) {
291+
const token = tokens[i];
292+
293+
// Skip tokens that are part of compound sequences
294+
if (compoundTokenIndices.has(i)) {
295+
continue;
296+
}
297+
298+
// Skip START_EVENT and END_EVENT tokens
299+
if (token.type === TokenType.START_EVENT || token.type === TokenType.END_EVENT) {
300+
continue;
301+
}
302+
303+
// Validate token range
304+
if (token.start < 0 || token.end > docLength || token.start >= token.end) {
305+
continue;
306+
}
307+
308+
const text = content.slice(token.start, token.end);
309+
callbacks.onToken(token, text);
310+
}
311+
};
312+
261313
export const chipPlugin = ViewPlugin.fromClass(
262314
class {
263315
decorations: RangeSet<Decoration>;
@@ -274,60 +326,35 @@ export const chipPlugin = ViewPlugin.fromClass(
274326
}
275327
}
276328
buildDecorations(view: EditorView) {
277-
const widgets = [];
329+
const widgets: any[] = []; // Type as any[] to allow pushing Range<Decoration>
278330
const { tokens, compounds } = view.state.field(tokenField);
279-
const docLength = view.state.doc.length;
280-
281-
// Create a set of token indices that are part of compounds
282-
const compoundTokenIndices = new Set<number>();
283-
for (const compound of compounds) {
284-
for (let i = compound.startIndex; i <= compound.endIndex; i++) {
285-
compoundTokenIndices.add(i);
286-
}
287-
}
288-
289-
// Render compound tokens as single chips
290-
for (const compound of compounds) {
291-
// Validate compound range
292-
if (compound.start < 0 || compound.end > docLength || compound.start >= compound.end) {
293-
continue;
294-
}
295-
296-
const chipType: TokenType = compound.tokenType;
297-
widgets.push(
298-
createChip(
299-
compound.displayText,
300-
chipType,
301-
compound.start,
302-
compound.end,
303-
view,
304-
compound.metadata
305-
).range(compound.start, compound.end)
306-
);
307-
}
308-
309-
// Render individual tokens that are not part of compounds
310-
for (let i = 0; i < tokens.length; i++) {
311-
const token = tokens[i];
312-
313-
// Skip tokens that are part of compound sequences
314-
if (compoundTokenIndices.has(i)) {
315-
continue;
316-
}
317-
318-
// Skip START_EVENT and END_EVENT tokens
319-
if (token.type === TokenType.START_EVENT || token.type === TokenType.END_EVENT) {
320-
continue;
321-
}
322-
323-
// Validate token range
324-
if (token.start < 0 || token.end > docLength || token.start >= token.end) {
325-
continue;
331+
const docContent = view.state.doc.toString();
332+
333+
iterateTokenStream(tokens, compounds, docContent, {
334+
onCompound: (compound) => {
335+
widgets.push(
336+
createChip(
337+
compound.displayText,
338+
compound.tokenType,
339+
compound.start,
340+
compound.end,
341+
view,
342+
compound.metadata
343+
).range(compound.start, compound.end)
344+
);
345+
},
346+
onToken: (token, text) => {
347+
widgets.push(
348+
createChip(
349+
text,
350+
token.type,
351+
token.start,
352+
token.end,
353+
view
354+
).range(token.start, token.end)
355+
);
326356
}
327-
328-
const text = view.state.doc.sliceString(token.start, token.end);
329-
widgets.push(createChip(text, token.type, token.start, token.end, view).range(token.start, token.end));
330-
}
357+
});
331358

332359
return Decoration.set(widgets, true);
333360
}

0 commit comments

Comments
 (0)