implements PLN-0018
This commit is contained in:
parent
c6b711f3fa
commit
983cbd5fe8
@ -10,4 +10,4 @@
|
||||
{"type":"discussion","id":"DSC-0009","status":"open","ticket":"studio-debugger-workspace-integration","title":"Integrate ../debugger into Studio as a dedicated workspace","created_at":"2026-03-30","updated_at":"2026-03-30","tags":["studio","debugger","workspace","integration","shell"],"agendas":[{"id":"AGD-0009","file":"AGD-0009-studio-debugger-workspace-integration.md","status":"open","created_at":"2026-03-30","updated_at":"2026-03-30"}],"decisions":[],"plans":[],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0010","status":"done","ticket":"studio-code-editor-workspace-foundations","title":"Establish Code Editor workspace foundations in Studio without LSP","created_at":"2026-03-30","updated_at":"2026-03-31","tags":["studio","editor","workspace","multi-frontend","lsp-deferred"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0026","file":"discussion/lessons/DSC-0010-studio-code-editor-workspace-foundations/LSN-0026-read-only-editor-foundations-and-semantic-deferral.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31"}]}
|
||||
{"type":"discussion","id":"DSC-0011","status":"done","ticket":"compiler-analyze-compile-build-pipeline-split","title":"Split compiler pipeline into analyze, compile, and build entrypoints","created_at":"2026-03-30","updated_at":"2026-03-30","tags":["compiler","pipeline","artifacts","build","analysis"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0025","file":"discussion/lessons/DSC-0011-compiler-analyze-compile-build-pipeline-split/LSN-0025-compiler-pipeline-entrypoints-and-result-boundaries.md","status":"done","created_at":"2026-03-30","updated_at":"2026-03-30"}]}
|
||||
{"type":"discussion","id":"DSC-0012","status":"open","ticket":"studio-editor-document-vfs-boundary","title":"Definir um boundary de VFS documental para tree/view/open files no Code Editor do Studio","created_at":"2026-03-31","updated_at":"2026-03-31","tags":["studio","editor","workspace","vfs","filesystem","boundary"],"agendas":[{"id":"AGD-0012","file":"AGD-0012-studio-editor-document-vfs-boundary.md","status":"in_progress","created_at":"2026-03-31","updated_at":"2026-03-31"}],"decisions":[{"id":"DEC-0009","file":"DEC-0009-studio-prometeu-vfs-project-document-boundary.md","status":"in_progress","created_at":"2026-03-31","updated_at":"2026-03-31","ref_agenda":"AGD-0012"}],"plans":[{"id":"PLN-0015","file":"PLN-0015-propagate-dec-0009-into-studio-specs.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0009"]},{"id":"PLN-0016","file":"PLN-0016-build-prometeu-vfs-filesystem-backed-core.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0009"]},{"id":"PLN-0017","file":"PLN-0017-add-studio-project-session-ownership-for-prometeu-vfs.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0009"]},{"id":"PLN-0018","file":"PLN-0018-migrate-code-editor-to-prometeu-vfs.md","status":"review","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0009"]}],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0012","status":"open","ticket":"studio-editor-document-vfs-boundary","title":"Definir um boundary de VFS documental para tree/view/open files no Code Editor do Studio","created_at":"2026-03-31","updated_at":"2026-03-31","tags":["studio","editor","workspace","vfs","filesystem","boundary"],"agendas":[{"id":"AGD-0012","file":"AGD-0012-studio-editor-document-vfs-boundary.md","status":"in_progress","created_at":"2026-03-31","updated_at":"2026-03-31"}],"decisions":[{"id":"DEC-0009","file":"DEC-0009-studio-prometeu-vfs-project-document-boundary.md","status":"in_progress","created_at":"2026-03-31","updated_at":"2026-03-31","ref_agenda":"AGD-0012"}],"plans":[{"id":"PLN-0015","file":"PLN-0015-propagate-dec-0009-into-studio-specs.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0009"]},{"id":"PLN-0016","file":"PLN-0016-build-prometeu-vfs-filesystem-backed-core.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0009"]},{"id":"PLN-0017","file":"PLN-0017-add-studio-project-session-ownership-for-prometeu-vfs.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0009"]},{"id":"PLN-0018","file":"PLN-0018-migrate-code-editor-to-prometeu-vfs.md","status":"done","created_at":"2026-03-31","updated_at":"2026-03-31","ref_decisions":["DEC-0009"]}],"lessons":[]}
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
id: PLN-0018
|
||||
ticket: studio-editor-document-vfs-boundary
|
||||
title: Migrate the Code Editor workspace to consume `prometeu-vfs`
|
||||
status: review
|
||||
status: done
|
||||
created: 2026-03-31
|
||||
completed:
|
||||
completed: 2026-03-31
|
||||
tags:
|
||||
- studio
|
||||
- editor
|
||||
|
||||
@ -28,7 +28,7 @@ public final class MainView extends BorderPane {
|
||||
setTop(new StudioShellTopBarControl(menuBar));
|
||||
|
||||
host.register(new AssetWorkspace(projectReference));
|
||||
host.register(new EditorWorkspace(projectReference));
|
||||
host.register(new EditorWorkspace(projectReference, projectSession.projectDocumentVfs()));
|
||||
// host.register(new PlaceholderWorkspace(WorkspaceId.DEBUG, I18n.WORKSPACE_DEBUG, "Debug"));
|
||||
host.register(new ShipperWorkspace(projectReference));
|
||||
|
||||
|
||||
@ -1,63 +0,0 @@
|
||||
package p.studio.workspaces.editor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.nio.charset.CodingErrorAction;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public final class EditorFileBufferLoader {
|
||||
|
||||
public Optional<EditorOpenFileBuffer> load(final Path path) {
|
||||
final var normalizedPath = Objects.requireNonNull(path, "path").toAbsolutePath().normalize();
|
||||
if (!Files.isRegularFile(normalizedPath)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
final byte[] bytes;
|
||||
try {
|
||||
bytes = Files.readAllBytes(normalizedPath);
|
||||
} catch (IOException ioException) {
|
||||
throw new UncheckedIOException(ioException);
|
||||
}
|
||||
|
||||
if (containsNulByte(bytes)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
final String content;
|
||||
try {
|
||||
content = StandardCharsets.UTF_8.newDecoder()
|
||||
.onMalformedInput(CodingErrorAction.REPORT)
|
||||
.onUnmappableCharacter(CodingErrorAction.REPORT)
|
||||
.decode(ByteBuffer.wrap(bytes))
|
||||
.toString();
|
||||
} catch (CharacterCodingException codingException) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(new EditorOpenFileBuffer(
|
||||
normalizedPath,
|
||||
normalizedPath.getFileName().toString(),
|
||||
content,
|
||||
lineSeparator(content)));
|
||||
}
|
||||
|
||||
private boolean containsNulByte(final byte[] bytes) {
|
||||
for (final byte value : bytes) {
|
||||
if (value == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String lineSeparator(final String content) {
|
||||
return content.contains("\r\n") ? "CRLF" : "LF";
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package p.studio.workspaces.editor;
|
||||
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Tooltip;
|
||||
@ -12,9 +13,10 @@ import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.Node;
|
||||
import p.studio.Container;
|
||||
import p.studio.utilities.i18n.I18n;
|
||||
import p.studio.vfs.VfsProjectNode;
|
||||
import p.studio.vfs.VfsProjectSnapshot;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashSet;
|
||||
@ -26,12 +28,12 @@ import java.util.function.Consumer;
|
||||
public final class EditorProjectNavigatorPanel extends BorderPane {
|
||||
private final Button revealActiveButton = new Button();
|
||||
private final Button refreshButton = new Button();
|
||||
private final TreeView<EditorProjectNode> treeView = new TreeView<>();
|
||||
private final TreeView<VfsProjectNode> treeView = new TreeView<>();
|
||||
private final Label emptyState = emptyLabel();
|
||||
private final StackPane content = new StackPane();
|
||||
private Runnable revealActiveFileAction = () -> { };
|
||||
private Runnable refreshAction = () -> { };
|
||||
private Consumer<EditorProjectNode> fileSelectionAction = node -> { };
|
||||
private Consumer<VfsProjectNode> fileSelectionAction = node -> { };
|
||||
|
||||
public EditorProjectNavigatorPanel() {
|
||||
getStyleClass().addAll("editor-workspace-panel", "editor-workspace-navigator-panel");
|
||||
@ -82,7 +84,7 @@ public final class EditorProjectNavigatorPanel extends BorderPane {
|
||||
treeView.setFocusTraversable(false);
|
||||
treeView.setCellFactory(ignored -> new TreeCell<>() {
|
||||
@Override
|
||||
protected void updateItem(final EditorProjectNode item, final boolean empty) {
|
||||
protected void updateItem(final VfsProjectNode item, final boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
getStyleClass().remove("editor-workspace-tree-cell-tagged");
|
||||
getStyleClass().remove("editor-workspace-tree-cell-root");
|
||||
@ -121,11 +123,11 @@ public final class EditorProjectNavigatorPanel extends BorderPane {
|
||||
revealActiveButton.setDisable(!available);
|
||||
}
|
||||
|
||||
public void setFileSelectionAction(final Consumer<EditorProjectNode> fileSelectionAction) {
|
||||
public void setFileSelectionAction(final Consumer<VfsProjectNode> fileSelectionAction) {
|
||||
this.fileSelectionAction = Objects.requireNonNull(fileSelectionAction, "fileSelectionAction");
|
||||
}
|
||||
|
||||
public void setSnapshot(final EditorProjectSnapshot snapshot) {
|
||||
public void setSnapshot(final VfsProjectSnapshot snapshot) {
|
||||
final var currentSelection = selectedPath();
|
||||
final var expandedPaths = captureExpandedPaths(treeView.getRoot());
|
||||
final var rootItem = buildTreeItem(Objects.requireNonNull(snapshot, "snapshot").rootNode(), expandedPaths, true);
|
||||
@ -147,11 +149,11 @@ public final class EditorProjectNavigatorPanel extends BorderPane {
|
||||
});
|
||||
}
|
||||
|
||||
private TreeItem<EditorProjectNode> buildTreeItem(
|
||||
final EditorProjectNode node,
|
||||
private TreeItem<VfsProjectNode> buildTreeItem(
|
||||
final VfsProjectNode node,
|
||||
final Set<Path> expandedPaths,
|
||||
final boolean isRoot) {
|
||||
final TreeItem<EditorProjectNode> item = node.directory()
|
||||
final TreeItem<VfsProjectNode> item = node.directory()
|
||||
? new DirectoryTreeItem(node)
|
||||
: new TreeItem<>(node);
|
||||
item.setExpanded(isRoot || expandedPaths.contains(node.path()));
|
||||
@ -161,7 +163,7 @@ public final class EditorProjectNavigatorPanel extends BorderPane {
|
||||
return item;
|
||||
}
|
||||
|
||||
private Set<Path> captureExpandedPaths(final TreeItem<EditorProjectNode> item) {
|
||||
private Set<Path> captureExpandedPaths(final TreeItem<VfsProjectNode> item) {
|
||||
final var expandedPaths = new HashSet<Path>();
|
||||
if (item == null) {
|
||||
return expandedPaths;
|
||||
@ -170,7 +172,7 @@ public final class EditorProjectNavigatorPanel extends BorderPane {
|
||||
return expandedPaths;
|
||||
}
|
||||
|
||||
private void collectExpandedPaths(final TreeItem<EditorProjectNode> item, final Set<Path> expandedPaths) {
|
||||
private void collectExpandedPaths(final TreeItem<VfsProjectNode> item, final Set<Path> expandedPaths) {
|
||||
final var value = item.getValue();
|
||||
if (value != null && item.isExpanded()) {
|
||||
expandedPaths.add(value.path());
|
||||
@ -181,10 +183,10 @@ public final class EditorProjectNavigatorPanel extends BorderPane {
|
||||
private Optional<Path> selectedPath() {
|
||||
return Optional.ofNullable(treeView.getSelectionModel().getSelectedItem())
|
||||
.map(TreeItem::getValue)
|
||||
.map(EditorProjectNode::path);
|
||||
.map(VfsProjectNode::path);
|
||||
}
|
||||
|
||||
private void restoreSelection(final TreeItem<EditorProjectNode> rootItem, final Optional<Path> selectedPath) {
|
||||
private void restoreSelection(final TreeItem<VfsProjectNode> rootItem, final Optional<Path> selectedPath) {
|
||||
treeView.getSelectionModel().clearSelection();
|
||||
if (selectedPath.isEmpty()) {
|
||||
return;
|
||||
@ -193,7 +195,7 @@ public final class EditorProjectNavigatorPanel extends BorderPane {
|
||||
.ifPresent(item -> treeView.getSelectionModel().select(item));
|
||||
}
|
||||
|
||||
private Optional<TreeItem<EditorProjectNode>> findTreeItem(final TreeItem<EditorProjectNode> item, final Path path) {
|
||||
private Optional<TreeItem<VfsProjectNode>> findTreeItem(final TreeItem<VfsProjectNode> item, final Path path) {
|
||||
if (item == null || item.getValue() == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
@ -216,15 +218,15 @@ public final class EditorProjectNavigatorPanel extends BorderPane {
|
||||
emptyState.setManaged(empty);
|
||||
}
|
||||
|
||||
private void expandAncestors(final TreeItem<EditorProjectNode> item) {
|
||||
TreeItem<EditorProjectNode> current = item;
|
||||
private void expandAncestors(final TreeItem<VfsProjectNode> item) {
|
||||
TreeItem<VfsProjectNode> current = item;
|
||||
while (current != null) {
|
||||
current.setExpanded(true);
|
||||
current = current.getParent();
|
||||
}
|
||||
}
|
||||
|
||||
private Node iconFor(final EditorProjectNode node, final TreeItem<EditorProjectNode> treeItem) {
|
||||
private Node iconFor(final VfsProjectNode node, final TreeItem<VfsProjectNode> treeItem) {
|
||||
if (node.directory()) {
|
||||
if (isBuildTone(node)) {
|
||||
return EditorWorkspaceIcons.folderBuild();
|
||||
@ -241,7 +243,7 @@ public final class EditorProjectNavigatorPanel extends BorderPane {
|
||||
return EditorWorkspaceIcons.file();
|
||||
}
|
||||
|
||||
private String labelFor(final EditorProjectNode node, final TreeItem<EditorProjectNode> treeItem) {
|
||||
private String labelFor(final VfsProjectNode node, final TreeItem<VfsProjectNode> treeItem) {
|
||||
if (!isRoot(treeItem)) {
|
||||
return node.displayName();
|
||||
}
|
||||
@ -252,7 +254,7 @@ public final class EditorProjectNavigatorPanel extends BorderPane {
|
||||
return directoryName + " (" + node.displayName() + ")";
|
||||
}
|
||||
|
||||
private boolean isInsideTaggedSourceRoot(final TreeItem<EditorProjectNode> item) {
|
||||
private boolean isInsideTaggedSourceRoot(final TreeItem<VfsProjectNode> item) {
|
||||
var current = item == null ? null : item.getParent();
|
||||
while (current != null && current.getValue() != null) {
|
||||
if (current.getValue().taggedSourceRoot()) {
|
||||
@ -263,11 +265,11 @@ public final class EditorProjectNavigatorPanel extends BorderPane {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isRoot(final TreeItem<EditorProjectNode> item) {
|
||||
private boolean isRoot(final TreeItem<VfsProjectNode> item) {
|
||||
return item != null && item.getParent() == null;
|
||||
}
|
||||
|
||||
private boolean isBuildTone(final EditorProjectNode node) {
|
||||
private boolean isBuildTone(final VfsProjectNode node) {
|
||||
return node.directory()
|
||||
&& ("build".equals(node.displayName()) || "cartridge".equals(node.displayName()));
|
||||
}
|
||||
@ -280,8 +282,8 @@ public final class EditorProjectNavigatorPanel extends BorderPane {
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
private static final class DirectoryTreeItem extends TreeItem<EditorProjectNode> {
|
||||
private DirectoryTreeItem(final EditorProjectNode node) {
|
||||
private static final class DirectoryTreeItem extends TreeItem<VfsProjectNode> {
|
||||
private DirectoryTreeItem(final VfsProjectNode node) {
|
||||
super(node);
|
||||
}
|
||||
|
||||
|
||||
@ -1,18 +0,0 @@
|
||||
package p.studio.workspaces.editor;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public record EditorProjectNode(
|
||||
Path path,
|
||||
String displayName,
|
||||
boolean directory,
|
||||
boolean taggedSourceRoot,
|
||||
List<EditorProjectNode> children) {
|
||||
public EditorProjectNode {
|
||||
path = Objects.requireNonNull(path, "path").toAbsolutePath().normalize();
|
||||
displayName = Objects.requireNonNull(displayName, "displayName");
|
||||
children = List.copyOf(children);
|
||||
}
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
package p.studio.workspaces.editor;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
|
||||
public record EditorProjectSnapshot(
|
||||
Path projectRoot,
|
||||
EditorProjectNode rootNode) {
|
||||
public EditorProjectSnapshot {
|
||||
projectRoot = Objects.requireNonNull(projectRoot, "projectRoot").toAbsolutePath().normalize();
|
||||
rootNode = Objects.requireNonNull(rootNode, "rootNode");
|
||||
}
|
||||
}
|
||||
@ -1,62 +0,0 @@
|
||||
package p.studio.workspaces.editor;
|
||||
|
||||
import p.studio.compiler.FrontendRegistryService;
|
||||
import p.studio.projects.ProjectReference;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class EditorProjectSnapshotService {
|
||||
|
||||
public EditorProjectSnapshot createSnapshot(final ProjectReference projectReference) {
|
||||
final var project = Objects.requireNonNull(projectReference, "projectReference");
|
||||
final var projectRoot = project.rootPath().toAbsolutePath().normalize();
|
||||
final var taggedRoots = taggedSourceRoots(project);
|
||||
final var rootNode = buildNode(projectRoot, project.name(), taggedRoots);
|
||||
return new EditorProjectSnapshot(projectRoot, rootNode);
|
||||
}
|
||||
|
||||
private Set<Path> taggedSourceRoots(final ProjectReference projectReference) {
|
||||
return FrontendRegistryService.getFrontendSpec(projectReference.languageId())
|
||||
.stream()
|
||||
.flatMap(frontendSpec -> frontendSpec.getSourceRoots().stream())
|
||||
.map(projectReference.rootPath()::resolve)
|
||||
.map(Path::toAbsolutePath)
|
||||
.map(Path::normalize)
|
||||
.collect(java.util.stream.Collectors.toSet());
|
||||
}
|
||||
|
||||
private EditorProjectNode buildNode(
|
||||
final Path path,
|
||||
final String displayName,
|
||||
final Set<Path> taggedRoots) {
|
||||
if (!Files.isDirectory(path)) {
|
||||
return new EditorProjectNode(path, displayName, false, taggedRoots.contains(path), List.of());
|
||||
}
|
||||
|
||||
final List<EditorProjectNode> children;
|
||||
try (Stream<Path> stream = Files.list(path)) {
|
||||
children = stream
|
||||
.sorted(nodeComparator())
|
||||
.map(child -> buildNode(child, child.getFileName().toString(), taggedRoots))
|
||||
.toList();
|
||||
} catch (IOException ioException) {
|
||||
throw new UncheckedIOException(ioException);
|
||||
}
|
||||
|
||||
return new EditorProjectNode(path, displayName, true, taggedRoots.contains(path), children);
|
||||
}
|
||||
|
||||
private Comparator<Path> nodeComparator() {
|
||||
return Comparator
|
||||
.comparing((Path path) -> !Files.isDirectory(path))
|
||||
.thenComparing(path -> path.getFileName().toString(), String.CASE_INSENSITIVE_ORDER);
|
||||
}
|
||||
}
|
||||
@ -12,6 +12,12 @@ import p.studio.projects.ProjectReference;
|
||||
import p.studio.utilities.i18n.I18n;
|
||||
import p.studio.workspaces.Workspace;
|
||||
import p.studio.workspaces.WorkspaceId;
|
||||
import p.studio.vfs.ProjectDocumentVfs;
|
||||
import p.studio.vfs.VfsDocumentOpenResult;
|
||||
import p.studio.vfs.VfsProjectNode;
|
||||
import p.studio.vfs.VfsTextDocument;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public final class EditorWorkspace extends Workspace {
|
||||
private final BorderPane root = new BorderPane();
|
||||
@ -21,12 +27,14 @@ public final class EditorWorkspace extends Workspace {
|
||||
private final EditorHelperPanel helperPanel = new EditorHelperPanel();
|
||||
private final EditorStatusBar statusBar = new EditorStatusBar();
|
||||
private final EditorTabStrip tabStrip = new EditorTabStrip();
|
||||
private final EditorProjectSnapshotService snapshotService = new EditorProjectSnapshotService();
|
||||
private final EditorFileBufferLoader fileBufferLoader = new EditorFileBufferLoader();
|
||||
private final ProjectDocumentVfs projectDocumentVfs;
|
||||
private final EditorOpenFileSession openFileSession = new EditorOpenFileSession();
|
||||
|
||||
public EditorWorkspace(final ProjectReference projectReference) {
|
||||
public EditorWorkspace(
|
||||
final ProjectReference projectReference,
|
||||
final ProjectDocumentVfs projectDocumentVfs) {
|
||||
super(projectReference);
|
||||
this.projectDocumentVfs = Objects.requireNonNull(projectDocumentVfs, "projectDocumentVfs");
|
||||
root.getStyleClass().add("editor-workspace");
|
||||
codeArea.setParagraphGraphicFactory(LineNumberFactory.get(codeArea));
|
||||
codeArea.setEditable(false);
|
||||
@ -62,16 +70,24 @@ public final class EditorWorkspace extends Workspace {
|
||||
public CodeArea codeArea() { return codeArea; }
|
||||
|
||||
private void refreshNavigator() {
|
||||
navigatorPanel.setSnapshot(snapshotService.createSnapshot(projectReference));
|
||||
navigatorPanel.setSnapshot(projectDocumentVfs.refresh());
|
||||
}
|
||||
|
||||
private void openNode(final EditorProjectNode node) {
|
||||
fileBufferLoader.load(node.path())
|
||||
.ifPresentOrElse(this::openFile, () -> showUnsupportedFileModal(node.path()));
|
||||
private void openNode(final VfsProjectNode node) {
|
||||
final VfsDocumentOpenResult result = projectDocumentVfs.openDocument(node.path());
|
||||
if (result instanceof VfsTextDocument textDocument) {
|
||||
openFile(textDocument);
|
||||
return;
|
||||
}
|
||||
showUnsupportedFileModal(node.path());
|
||||
}
|
||||
|
||||
private void openFile(final EditorOpenFileBuffer fileBuffer) {
|
||||
openFileSession.open(fileBuffer);
|
||||
private void openFile(final VfsTextDocument textDocument) {
|
||||
openFileSession.open(new EditorOpenFileBuffer(
|
||||
textDocument.path(),
|
||||
textDocument.documentName(),
|
||||
textDocument.content(),
|
||||
textDocument.lineSeparator()));
|
||||
renderSession();
|
||||
}
|
||||
|
||||
|
||||
@ -1,37 +0,0 @@
|
||||
package p.studio.workspaces.editor;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
final class EditorFileBufferLoaderTest {
|
||||
@TempDir
|
||||
Path tempDir;
|
||||
|
||||
@Test
|
||||
void loadReturnsReadOnlyTextBufferForUtf8TextFile() throws Exception {
|
||||
final var file = tempDir.resolve("main.pbs");
|
||||
Files.writeString(file, "fn main(): void\n");
|
||||
|
||||
final var result = new EditorFileBufferLoader().load(file);
|
||||
|
||||
assertTrue(result.isPresent());
|
||||
assertEquals("main.pbs", result.get().tabLabel());
|
||||
assertEquals("LF", result.get().lineSeparator());
|
||||
assertTrue(result.get().content().contains("fn main()"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void loadRejectsBinaryLikeFile() throws Exception {
|
||||
final var file = tempDir.resolve("sprite.bin");
|
||||
Files.write(file, new byte[]{0x01, 0x00, 0x02});
|
||||
|
||||
final var result = new EditorFileBufferLoader().load(file);
|
||||
|
||||
assertTrue(result.isEmpty());
|
||||
}
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
package p.studio.workspaces.editor;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import p.studio.projects.ProjectReference;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
final class EditorProjectSnapshotServiceTest {
|
||||
@TempDir
|
||||
Path tempDir;
|
||||
|
||||
@Test
|
||||
void snapshotIncludesHiddenFilesOrdersFoldersFirstAndTagsSourceRoots() throws Exception {
|
||||
Files.createDirectories(tempDir.resolve("src"));
|
||||
Files.createDirectories(tempDir.resolve("assets"));
|
||||
Files.writeString(tempDir.resolve(".env"), "TOKEN=1\n");
|
||||
Files.writeString(tempDir.resolve("README.md"), "# project\n");
|
||||
|
||||
final var snapshot = new EditorProjectSnapshotService().createSnapshot(new ProjectReference(
|
||||
"Example",
|
||||
"1.0.0",
|
||||
"pbs",
|
||||
1,
|
||||
tempDir));
|
||||
|
||||
assertEquals("Example", snapshot.rootNode().displayName());
|
||||
assertEquals("assets", snapshot.rootNode().children().get(0).displayName());
|
||||
assertEquals("src", snapshot.rootNode().children().get(1).displayName());
|
||||
assertEquals(".env", snapshot.rootNode().children().get(2).displayName());
|
||||
assertTrue(snapshot.rootNode().children().get(1).taggedSourceRoot());
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user