Skip to content
Open
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
39 changes: 36 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'
import parserHTML from 'prettier/plugins/html'
import clsx from 'clsx'
import XsdFeatures from 'monaco-xsd-code-completion/esm/XsdFeatures'
import 'monaco-xsd-code-completion/src/style.css'
Expand Down Expand Up @@ -159,6 +161,14 @@ function isConfigurationFile(fileExtension: string) {
return fileExtension === 'xml'
}

function prettierFormat(xml: string): Promise<string> {
return prettier.format(xml, {
parser: 'html',
plugins: [parserHTML],
tabWidth: 3,
})
Comment on lines +164 to +169
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

export default function CodeEditor() {
const theme = useTheme()
const project = useProjectStore.getState().project
Expand Down Expand Up @@ -217,7 +227,6 @@ export default function CodeEditor() {
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 @@ -290,6 +299,18 @@ export default function CodeEditor() {
)
}, [])

const runPrettierReformat = async () => {
const editor = editorReference.current
if (!editor) return
const model = editor.getModel()
if (!model) return
try {
if (model) model.setValue(await prettierFormat(model.getValue()))
} catch (error) {
console.error('Failed to reformat XML:', error)
}
}

const runSchemaValidation = useCallback(
async (content: string) => {
const monaco = monacoReference.current
Expand Down Expand Up @@ -374,7 +395,6 @@ export default function CodeEditor() {

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

fetchFrankConfigXsd()
.then((xsdContent) => {
Expand Down Expand Up @@ -422,6 +442,19 @@ export default function CodeEditor() {
}
},
})

editor.addAction({
id: 'reformat-xml-prettier',
label: 'Reformat',
contextMenuGroupId: 'navigation',
contextMenuOrder: 3,
keybindings: [
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The key combination needs to be added to the Help page.

I also saw that there are a lot of other combinations in the editor that are not noted there.

monacoReference.current.KeyMod.CtrlCmd |
monacoReference.current.KeyMod.Shift |
monacoReference.current.KeyCode.KeyR,
Comment on lines +452 to +454
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of editors use Alt + Shift + F or Crtl + Shift + F. IntelliJ uses Ctrl + Alt + L

I think it would be nice to stick with one of those, for people who have the muscle memory for it already.

],
run: runPrettierReformat,
})
}

useEffect(() => {
Expand Down Expand Up @@ -642,7 +675,7 @@ export default function CodeEditor() {
scheduleSave()
if (value && fileLanguage === 'xml') scheduleSchemaValidation(value)
}}
options={{ automaticLayout: true, quickSuggestions: false }}
options={{ automaticLayout: true, quickSuggestions: false, tabSize: 3 }}
/>
</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 Expand Up @@ -591,7 +591,7 @@
addNodeAtPosition(position, parsedData.name)
}

const onDragEnd = (event: React.DragEvent) => {

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

View workflow job for this annotation

GitHub Actions / Build & Run All Tests

'event' is defined but never used. Allowed unused args must match /^_/u
setDraggedName(null)
setParentId(null)
}
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,16 @@ 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);
String formatted = XmlConfigurationUtils.convertNodeToString(document);

Comment on lines +53 to +55
// Just write to the disk. ProjectService reads directly from disk now!
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 +73,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