diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/themes/pbs/semantic-highlighting.css b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/themes/pbs/semantic-highlighting.css index 5d873b0e..cf0681c8 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/themes/pbs/semantic-highlighting.css +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/resources/themes/pbs/semantic-highlighting.css @@ -101,7 +101,7 @@ } .editor-workspace-status-chip-type-pbs { - -fx-background-color: #1e2d3b; - -fx-border-color: #4aa3ff; - -fx-text-fill: #dce9f8; + -fx-background-color: #2c2140; + -fx-border-color: #a47dff; + -fx-text-fill: #efe6ff; } diff --git a/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorStatusBar.java b/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorStatusBar.java index fc814890..8756c025 100644 --- a/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorStatusBar.java +++ b/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorStatusBar.java @@ -15,6 +15,7 @@ import java.nio.file.Path; import java.util.Optional; public final class EditorStatusBar extends HBox { + private static final String DEFAULT_INDENTATION = "Spaces: 4"; private final HBox breadcrumb = new HBox(6); private final Label position = new Label(); private final Label lineSeparator = new Label(); @@ -61,9 +62,8 @@ public final class EditorStatusBar extends HBox { final EditorDocumentPresentation presentation) { showBreadcrumb(projectReference, fileBuffer.path()); showMetadata(true); - bindDefault(position, I18n.CODE_EDITOR_STATUS_POSITION); - setText(lineSeparator, fileBuffer.lineSeparator()); - bindDefault(indentation, I18n.CODE_EDITOR_STATUS_INDENTATION); + showPosition(fileBuffer.editable(), 1, 1); + showDocumentFormatting(fileBuffer.lineSeparator(), fileBuffer.content()); setText(language, fileBuffer.typeId()); EditorDocumentPresentationStyles.applyToStatusChip(language, presentation); showAccessMode(fileBuffer.readOnly()); @@ -72,7 +72,7 @@ public final class EditorStatusBar extends HBox { public void showPlaceholder(final EditorDocumentPresentation presentation) { breadcrumb.getChildren().clear(); showMetadata(false); - bindDefault(position, I18n.CODE_EDITOR_STATUS_POSITION); + showPosition(false, 1, 1); bindDefault(lineSeparator, I18n.CODE_EDITOR_STATUS_LINE_SEPARATOR); bindDefault(indentation, I18n.CODE_EDITOR_STATUS_INDENTATION); bindDefault(language, I18n.CODE_EDITOR_STATUS_LANGUAGE); @@ -81,6 +81,18 @@ public final class EditorStatusBar extends HBox { accessMode.getStyleClass().removeAll("editor-workspace-status-chip-editable", "editor-workspace-status-chip-read-only"); } + public void showCaretPosition(final int line, final int column) { + if (!position.isManaged()) { + return; + } + setText(position, line + ":" + column); + } + + public void showDocumentFormatting(final String separator, final String content) { + setText(lineSeparator, separator); + setText(indentation, detectIndentation(content)); + } + private void bindDefault(final Label label, final I18n key) { bindText(label, key); } @@ -168,13 +180,21 @@ public final class EditorStatusBar extends HBox { } private void showMetadata(final boolean visible) { - setVisibleManaged(position, visible); setVisibleManaged(lineSeparator, visible); setVisibleManaged(indentation, visible); setVisibleManaged(language, visible); setVisibleManaged(accessMode, visible); } + private void showPosition(final boolean visible, final int line, final int column) { + setVisibleManaged(position, visible); + if (!visible) { + bindDefault(position, I18n.CODE_EDITOR_STATUS_POSITION); + return; + } + setText(position, line + ":" + column); + } + private void showAccessMode(final boolean readOnlyMode) { accessMode.getChildren().setAll(readOnlyMode ? EditorWorkspaceIcons.lockClosed() @@ -195,4 +215,58 @@ public final class EditorStatusBar extends HBox { label.getStyleClass().add("editor-workspace-status-chip"); } } + + private String detectIndentation(final String content) { + final String normalized = content == null ? "" : content.replace("\r\n", "\n").replace('\r', '\n'); + int spaceGcd = 0; + boolean sawTabs = false; + for (final String line : normalized.split("\n", -1)) { + if (line.isBlank()) { + continue; + } + int spaces = 0; + int tabs = 0; + while (spaces + tabs < line.length()) { + final char ch = line.charAt(spaces + tabs); + if (ch == ' ') { + if (tabs > 0) { + spaces = 0; + break; + } + spaces++; + continue; + } + if (ch == '\t') { + tabs++; + continue; + } + break; + } + if (tabs > 0 && spaces == 0) { + sawTabs = true; + continue; + } + if (spaces > 1) { + spaceGcd = spaceGcd == 0 ? spaces : gcd(spaceGcd, spaces); + } + } + if (sawTabs) { + return "Tabs: 1"; + } + if (spaceGcd > 0) { + return "Spaces: " + spaceGcd; + } + return DEFAULT_INDENTATION; + } + + private int gcd(final int left, final int right) { + int a = Math.abs(left); + int b = Math.abs(right); + while (b != 0) { + final int next = a % b; + a = b; + b = next; + } + return a == 0 ? 1 : a; + } } diff --git a/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorWorkspace.java b/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorWorkspace.java index 92a1dccf..391dbc62 100644 --- a/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorWorkspace.java +++ b/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorWorkspace.java @@ -75,8 +75,11 @@ public final class EditorWorkspace extends Workspace { codeArea.setEditable(false); codeArea.setWrapText(false); codeArea.textProperty().addListener((ignored, previous, current) -> syncActiveDocumentToVfs(current)); - codeArea.caretPositionProperty().addListener((ignored, previous, current) -> - updateActiveGuides(current == null ? 0 : current.intValue())); + codeArea.caretPositionProperty().addListener((ignored, previous, current) -> { + final int caretOffset = current == null ? 0 : current.intValue(); + updateActiveGuides(caretOffset); + refreshStatusBarCaret(); + }); codeArea.getStyleClass().add("editor-workspace-code-area"); codeArea.addEventFilter(KeyEvent.KEY_PRESSED, this::guardInlineHintMutation); codeArea.addEventFilter(KeyEvent.KEY_TYPED, this::guardInlineHintMutation); @@ -229,6 +232,7 @@ public final class EditorWorkspace extends Workspace { EditorDocumentPresentationStyles.applyToCodeArea(codeArea, presentation); refreshCommandSurfaces(fileBuffer); statusBar.showFile(projectReference, fileBuffer, presentation); + refreshStatusBarCaret(); refreshSemanticOutline(fileBuffer, analysis); } @@ -370,6 +374,7 @@ public final class EditorWorkspace extends Workspace { final String sourceContent = inlineHintProjection.stripDecorations(content); final VfsDocumentOpenResult.VfsTextDocument updatedDocument = vfsProjectDocument.updateDocument(activeFile.path(), sourceContent); openFileSession.open(bufferFrom(updatedDocument)); + statusBar.showDocumentFormatting(updatedDocument.lineSeparator(), updatedDocument.content()); tabStrip.showOpenFiles( openFileSession.openFiles(), openFileSession.activeFile().map(EditorOpenFileBuffer::path).orElse(null)); @@ -473,6 +478,14 @@ public final class EditorWorkspace extends Workspace { refreshParagraphGraphics(); } + private void refreshStatusBarCaret() { + openFileSession.activeFile() + .filter(EditorOpenFileBuffer::editable) + .ifPresent(ignored -> statusBar.showCaretPosition( + codeArea.getCurrentParagraph() + 1, + codeArea.getCaretColumn() + 1)); + } + private void guardInlineHintMutation(final KeyEvent event) { if (syncingEditor || !codeArea.isEditable()) { return; diff --git a/test-projects/fragments/run.sh b/test-projects/fragments/run.sh index 3f792c15..87315da6 100755 --- a/test-projects/fragments/run.sh +++ b/test-projects/fragments/run.sh @@ -4,4 +4,4 @@ set -e cp build/assets.pa cartridge cp build/program.pbx cartridge -./runtime/prometeu run cartridge +./runtime/prometeu run cartridge \ No newline at end of file