Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 46 additions & 3 deletions src/main/frontend/app/routes/editor/editor.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import RulerCrossPenIcon from '/icons/solar/Ruler Cross Pen.svg?react'
import Editor, { type Monaco, type OnMount } from '@monaco-editor/react'
import prettier from 'prettier/standalone'
import prettierPluginXml from '@prettier/plugin-xml'
type ITextModel = Monaco['editor']['ITextModel']
type FindMatch = Monaco['editor']['FindMatch']
type IModelDeltaDecoration = Monaco['editor']['IModelDeltaDecoration']
Expand Down Expand Up @@ -162,6 +164,14 @@
return fileExtension === 'xml'
}

function prettierFormat(xml: string): Promise<string> {
return prettier.format(xml, {
parser: 'xml',
plugins: [prettierPluginXml],
tabWidth: 2,
})
Comment thread
stijnpotters1 marked this conversation as resolved.
}

Check warning on line 174 in src/main/frontend/app/routes/editor/editor.tsx

View workflow job for this annotation

GitHub Actions / Build & Run All Tests

Delete `··`
async function validateFlow(content: string, model: ITextModel): Promise<ValidationError[]> {
const flowFragment = extractFlowElements(content)
if (!flowFragment) return []
Expand Down Expand Up @@ -228,7 +238,7 @@
]
}

export default function CodeEditor() {

Check warning on line 241 in src/main/frontend/app/routes/editor/editor.tsx

View workflow job for this annotation

GitHub Actions / Build & Run All Tests

Refactor this function to reduce its Cognitive Complexity from 16 to the 15 allowed
const theme = useTheme()
const project = useProjectStore.getState().project
const [activeTabFilePath, setActiveTabFilePath] = useState<string>(useEditorTabStore.getState().activeTabFilePath)
Expand Down Expand Up @@ -311,7 +321,6 @@
if (isConfigurationFile(fileExtension ?? '')) {
saveConfiguration(project.name, configPath, updatedContent)
.then(({ xmlContent }) => {
setFileContent(xmlContent)
contentCacheRef.current.set(activeTabFilePath, { type: 'xml', content: xmlContent })
finishSaving()
if (project.isGitRepository) refreshOpenDiffs(project.name)
Expand Down Expand Up @@ -384,6 +393,28 @@
)
}, [])

const runPrettierReformat = async () => {
const editor = editorReference.current
if (!editor) return
const model = editor.getModel()
if (!model) return
try {
const formattedValue = await prettierFormat(model.getValue())
if (formattedValue === model.getValue()) return

const selection = editor.getSelection()
editor.pushUndoStop()
editor.executeEdits(
'prettier-reformat',
[{ range: model.getFullModelRange(), text: formattedValue, forceMoveMarkers: true }],
selection ? [selection] : undefined,
)
editor.pushUndoStop()
} catch (error) {
console.error('Failed to reformat XML:', error)
}
}

const runSchemaValidation = useCallback(
async (content: string) => {
const editor = editorReference.current
Expand Down Expand Up @@ -431,7 +462,6 @@

xsdFeatures.addCompletion()
xsdFeatures.addGenerateAction()
xsdFeatures.addReformatAction()

fetchFrankConfigXsd()
.then((xsdContent) => {
Expand Down Expand Up @@ -481,6 +511,19 @@
}
},
})

editor.addAction({
id: 'reformat-xml-prettier',
label: 'Reformat',
contextMenuGroupId: 'navigation',
contextMenuOrder: 3,
keybindings: [
Comment thread
stijnpotters1 marked this conversation as resolved.
monacoReference.current.KeyMod.Alt |
monacoReference.current.KeyMod.Shift |
monacoReference.current.KeyCode.KeyF,
],
run: runPrettierReformat,
})
}

useEffect(() => {
Expand Down Expand Up @@ -708,7 +751,7 @@
applyFlowHighlighter() // Real-time highlight updates
}
}}
options={{ automaticLayout: true, quickSuggestions: false }}
options={{ automaticLayout: true, quickSuggestions: false, tabSize: 2, insertSpaces: true, detectIndentation: false }}

Check warning on line 754 in src/main/frontend/app/routes/editor/editor.tsx

View workflow job for this annotation

GitHub Actions / Build & Run All Tests

Replace `·automaticLayout:·true,·quickSuggestions:·false,·tabSize:·2,·insertSpaces:·true,·detectIndentation:·false` with `⏎····················automaticLayout:·true,⏎····················quickSuggestions:·false,⏎····················tabSize:·2,⏎····················insertSpaces:·true,⏎····················detectIndentation:·false,⏎·················`
/>
</div>
</>
Expand Down
2 changes: 1 addition & 1 deletion src/main/frontend/app/routes/studio/canvas/flow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@
await saveConfiguration(currentProject.name, configurationPath, updatedConfigXml)
clearConfigurationCache(currentProject.name, configurationPath)
useEditorTabStore.getState().refreshAllTabs()
if (currentProject.isGitRepository) refreshOpenDiffs(currentProject.name)
if (currentProject.isGitRepository) await refreshOpenDiffs(currentProject.name)

setSaveStatus('saved')
if (savedTimerRef.current) clearTimeout(savedTimerRef.current)
Expand All @@ -183,7 +183,7 @@
showErrorToast(`Failed to save XML: ${error instanceof Error ? error.message : error}`)
setSaveStatus('idle')
}
}, [project])

Check warning on line 186 in src/main/frontend/app/routes/studio/canvas/flow.tsx

View workflow job for this annotation

GitHub Actions / Build & Run All Tests

React Hook useCallback has an unnecessary dependency: 'project'. Either exclude it or remove the dependency array

const autosaveEnabled = useSettingsStore((s) => s.general.autoSave.enabled)
const autosaveDelay = useSettingsStore((s) => s.general.autoSave.delayMs)
Expand Down
8 changes: 8 additions & 0 deletions src/main/frontend/app/stores/shortcut-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,14 @@ export const ALL_SHORTCUTS: Omit<ShortcutDefinition, 'handler'>[] = [
modifiers: { cmdOrCtrl: true, shift: true },
displayOnly: true,
},
{
id: 'editor.reformat',
label: 'Reformat XML',
scope: 'editor',
key: 'f',
modifiers: { alt: true, shift: true },
displayOnly: true,
},
{ id: 'editor.search', label: 'Find', scope: 'editor', key: 'f', modifiers: { cmdOrCtrl: true }, displayOnly: true },
{
id: 'editor.replace',
Expand Down
1 change: 1 addition & 0 deletions src/main/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"eslint-plugin-sonarjs": "^3.0.7",
"eslint-plugin-unicorn": "^62.0.0",
"jsdom": "^27.4.0",
"@prettier/plugin-xml": "^3.4.1",
"prettier": "^3.8.1",
"prettier-plugin-tailwindcss": "^0.6.14",
"react-router-devtools": "^1.1.10",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ public boolean updateAdapter(Path configurationFile, String adapterName, String
String updatedXml = XmlConfigurationUtils.convertNodeToString(configDoc);
Files.writeString(absConfigFile, updatedXml, StandardCharsets.UTF_8, StandardOpenOption.TRUNCATE_EXISTING);
return true;

} catch (AdapterNotFoundException e) {
throw e;
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public ResponseEntity<XmlDTO> updateConfiguration(
public ResponseEntity<XmlDTO> addConfiguration(
@PathVariable String projectName,
@RequestParam String name
) throws ApiException, IOException {
) throws ApiException, IOException, TransformerException, ParserConfigurationException, SAXException {
String content = configurationService.addConfiguration(projectName, name);
XmlDTO xmlDTO = new XmlDTO(content);
return ResponseEntity.ok(xmlDTO);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,18 @@ public String updateConfiguration(String projectName, String filepath, String co
throw new ApiException("Invalid file path: " + filepath, HttpStatus.NOT_FOUND);
}

Document updatedDocument = XmlConfigurationUtils.insertFlowNamespace(content);
String updatedContent = XmlConfigurationUtils.convertNodeToString(updatedDocument);

fileSystemStorage.writeFile(absolutePath.toString(), updatedContent);
return updatedContent;
Document document = XmlConfigurationUtils.insertFlowNamespace(content);
if (document == null) {
throw new ApiException("Configuration content must not be blank", HttpStatus.BAD_REQUEST);
}

Comment thread
stijnpotters1 marked this conversation as resolved.
String formatted = XmlConfigurationUtils.convertNodeToString(document);
fileSystemStorage.writeFile(absolutePath.toString(), formatted);
return formatted;
}

public String addConfiguration(String projectName, String configurationName) throws IOException, ApiException {
public String addConfiguration(String projectName, String configurationName) throws IOException, ApiException, TransformerException, ParserConfigurationException, SAXException {
Project project = projectService.getProject(projectName);
Path absProjectPath = fileSystemStorage.toAbsolutePath(project.getRootPath());
Path configDir = absProjectPath.resolve(CONFIGURATIONS_DIR).normalize();
Expand All @@ -71,8 +75,11 @@ public String addConfiguration(String projectName, String configurationName) thr
}

String defaultXml = loadDefaultConfigurationXml();
fileSystemStorage.writeFile(filePath.toString(), defaultXml);
return defaultXml;
Document updatedDocument = XmlConfigurationUtils.insertFlowNamespace(defaultXml);
String updatedContent = XmlConfigurationUtils.convertNodeToString(updatedDocument);
fileSystemStorage.writeFile(filePath.toString(), updatedContent);

return updatedContent;
}

private String loadDefaultConfigurationXml() throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;

import java.io.IOException;
Expand Down Expand Up @@ -111,8 +112,9 @@ void updateConfiguration_Success() throws Exception {

configurationService.updateConfiguration("test", file.toString(), "<new/>");

assertEquals("<new/>\n", Files.readString(file, StandardCharsets.UTF_8));
verify(fileSystemStorage).writeFile(file.toString(), "<new/>\n");
String result = Files.readString(file, StandardCharsets.UTF_8).trim();
assertEquals("<new/>", result);
verify(fileSystemStorage).writeFile(eq(file.toString()), anyString());
}

@Test
Expand Down
Loading