prometeu-vsf
This commit is contained in:
parent
a13d0a54c2
commit
b7beef4c7e
@ -0,0 +1,24 @@
|
|||||||
|
package p.studio.workspaces.editor;
|
||||||
|
|
||||||
|
import org.fxmisc.richtext.model.StyleSpans;
|
||||||
|
import p.studio.workspaces.editor.syntaxhighlight.EditorDocumentSyntaxHighlighting;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
record EditorDocumentPresentation(
|
||||||
|
String styleKey,
|
||||||
|
List<String> stylesheetUrls,
|
||||||
|
EditorDocumentSyntaxHighlighting syntaxHighlighting) {
|
||||||
|
|
||||||
|
EditorDocumentPresentation {
|
||||||
|
styleKey = Objects.requireNonNull(styleKey, "styleKey");
|
||||||
|
stylesheetUrls = List.copyOf(Objects.requireNonNull(stylesheetUrls, "stylesheetUrls"));
|
||||||
|
syntaxHighlighting = Objects.requireNonNull(syntaxHighlighting, "syntaxHighlighting");
|
||||||
|
}
|
||||||
|
|
||||||
|
StyleSpans<Collection<String>> highlight(final String content) {
|
||||||
|
return syntaxHighlighting.highlight(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
package p.studio.workspaces.editor;
|
||||||
|
|
||||||
|
import p.studio.compiler.FrontendRegistryService;
|
||||||
|
import p.studio.vfs.VfsDocumentTypeIds;
|
||||||
|
import p.studio.workspaces.editor.syntaxhighlight.EditorDocumentSyntaxHighlighting;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
final class EditorDocumentPresentationRegistry {
|
||||||
|
private static final EditorDocumentPresentation TEXT_PRESENTATION = new EditorDocumentPresentation(
|
||||||
|
"text",
|
||||||
|
java.util.List.of(),
|
||||||
|
EditorDocumentSyntaxHighlighting.plainText());
|
||||||
|
private static final EditorDocumentPresentation JSON_PRESENTATION = new EditorDocumentPresentation(
|
||||||
|
"json",
|
||||||
|
java.util.List.of(stylesheet("presentations/json.css")),
|
||||||
|
EditorDocumentSyntaxHighlighting.json());
|
||||||
|
private static final EditorDocumentPresentation BASH_PRESENTATION = new EditorDocumentPresentation(
|
||||||
|
"bash",
|
||||||
|
java.util.List.of(stylesheet("presentations/bash.css")),
|
||||||
|
EditorDocumentSyntaxHighlighting.bash());
|
||||||
|
private static final EditorDocumentPresentation FRONTEND_PRESENTATION = new EditorDocumentPresentation(
|
||||||
|
"fe",
|
||||||
|
java.util.List.of(),
|
||||||
|
EditorDocumentSyntaxHighlighting.plainText());
|
||||||
|
|
||||||
|
EditorDocumentPresentation resolve(final String typeId) {
|
||||||
|
final String normalizedTypeId = normalize(typeId);
|
||||||
|
if (normalizedTypeId.isBlank()) {
|
||||||
|
return TEXT_PRESENTATION;
|
||||||
|
}
|
||||||
|
if (FrontendRegistryService.getFrontendSpec(normalizedTypeId).isPresent()) {
|
||||||
|
return FRONTEND_PRESENTATION;
|
||||||
|
}
|
||||||
|
if (VfsDocumentTypeIds.JSON.equals(normalizedTypeId)) {
|
||||||
|
return JSON_PRESENTATION;
|
||||||
|
}
|
||||||
|
if (VfsDocumentTypeIds.BASH.equals(normalizedTypeId)) {
|
||||||
|
return BASH_PRESENTATION;
|
||||||
|
}
|
||||||
|
return TEXT_PRESENTATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalize(final String typeId) {
|
||||||
|
return typeId == null ? "" : typeId.trim().toLowerCase(Locale.ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String stylesheet(final String relativePath) {
|
||||||
|
return java.util.Objects.requireNonNull(
|
||||||
|
EditorDocumentPresentationRegistry.class.getResource("/themes/editor/" + relativePath),
|
||||||
|
"missing editor presentation stylesheet: " + relativePath)
|
||||||
|
.toExternalForm();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package p.studio.workspaces.editor;
|
||||||
|
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
|
||||||
|
final class EditorDocumentPresentationStyles {
|
||||||
|
private static final String CODE_AREA_TYPE_CLASS_PREFIX = "editor-workspace-code-area-type-";
|
||||||
|
private static final String STATUS_CHIP_TYPE_CLASS_PREFIX = "editor-workspace-status-chip-type-";
|
||||||
|
|
||||||
|
private EditorDocumentPresentationStyles() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static void applyToCodeArea(final Node node, final EditorDocumentPresentation presentation) {
|
||||||
|
replaceTypeClass(node, CODE_AREA_TYPE_CLASS_PREFIX, presentation.styleKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void applyToStatusChip(final Label label, final EditorDocumentPresentation presentation) {
|
||||||
|
replaceTypeClass(label, STATUS_CHIP_TYPE_CLASS_PREFIX, presentation.styleKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void replaceTypeClass(
|
||||||
|
final Node node,
|
||||||
|
final String prefix,
|
||||||
|
final String styleKey) {
|
||||||
|
node.getStyleClass().removeIf(styleClass -> styleClass.startsWith(prefix));
|
||||||
|
node.getStyleClass().add(prefix + styleKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,11 +6,13 @@ import java.util.Objects;
|
|||||||
public record EditorOpenFileBuffer(
|
public record EditorOpenFileBuffer(
|
||||||
Path path,
|
Path path,
|
||||||
String tabLabel,
|
String tabLabel,
|
||||||
|
String typeId,
|
||||||
String content,
|
String content,
|
||||||
String lineSeparator) {
|
String lineSeparator) {
|
||||||
public EditorOpenFileBuffer {
|
public EditorOpenFileBuffer {
|
||||||
path = Objects.requireNonNull(path, "path").toAbsolutePath().normalize();
|
path = Objects.requireNonNull(path, "path").toAbsolutePath().normalize();
|
||||||
tabLabel = Objects.requireNonNull(tabLabel, "tabLabel");
|
tabLabel = Objects.requireNonNull(tabLabel, "tabLabel");
|
||||||
|
typeId = Objects.requireNonNull(typeId, "typeId");
|
||||||
content = Objects.requireNonNull(content, "content");
|
content = Objects.requireNonNull(content, "content");
|
||||||
lineSeparator = Objects.requireNonNull(lineSeparator, "lineSeparator");
|
lineSeparator = Objects.requireNonNull(lineSeparator, "lineSeparator");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,26 +53,30 @@ public final class EditorStatusBar extends HBox {
|
|||||||
readOnly
|
readOnly
|
||||||
);
|
);
|
||||||
|
|
||||||
showPlaceholder();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showFile(final ProjectReference projectReference, final EditorOpenFileBuffer fileBuffer) {
|
public void showFile(
|
||||||
|
final ProjectReference projectReference,
|
||||||
|
final EditorOpenFileBuffer fileBuffer,
|
||||||
|
final EditorDocumentPresentation presentation) {
|
||||||
showBreadcrumb(projectReference, fileBuffer.path());
|
showBreadcrumb(projectReference, fileBuffer.path());
|
||||||
showMetadata(true);
|
showMetadata(true);
|
||||||
bindDefault(position, I18n.CODE_EDITOR_STATUS_POSITION);
|
bindDefault(position, I18n.CODE_EDITOR_STATUS_POSITION);
|
||||||
setText(lineSeparator, fileBuffer.lineSeparator());
|
setText(lineSeparator, fileBuffer.lineSeparator());
|
||||||
bindDefault(indentation, I18n.CODE_EDITOR_STATUS_INDENTATION);
|
bindDefault(indentation, I18n.CODE_EDITOR_STATUS_INDENTATION);
|
||||||
setText(language, extensionText(fileBuffer.path()));
|
setText(language, fileBuffer.typeId());
|
||||||
|
EditorDocumentPresentationStyles.applyToStatusChip(language, presentation);
|
||||||
bindDefault(readOnly, I18n.CODE_EDITOR_STATUS_READ_ONLY);
|
bindDefault(readOnly, I18n.CODE_EDITOR_STATUS_READ_ONLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showPlaceholder() {
|
public void showPlaceholder(final EditorDocumentPresentation presentation) {
|
||||||
breadcrumb.getChildren().clear();
|
breadcrumb.getChildren().clear();
|
||||||
showMetadata(false);
|
showMetadata(false);
|
||||||
bindDefault(position, I18n.CODE_EDITOR_STATUS_POSITION);
|
bindDefault(position, I18n.CODE_EDITOR_STATUS_POSITION);
|
||||||
bindDefault(lineSeparator, I18n.CODE_EDITOR_STATUS_LINE_SEPARATOR);
|
bindDefault(lineSeparator, I18n.CODE_EDITOR_STATUS_LINE_SEPARATOR);
|
||||||
bindDefault(indentation, I18n.CODE_EDITOR_STATUS_INDENTATION);
|
bindDefault(indentation, I18n.CODE_EDITOR_STATUS_INDENTATION);
|
||||||
bindDefault(language, I18n.CODE_EDITOR_STATUS_LANGUAGE);
|
bindDefault(language, I18n.CODE_EDITOR_STATUS_LANGUAGE);
|
||||||
|
EditorDocumentPresentationStyles.applyToStatusChip(language, presentation);
|
||||||
bindDefault(readOnly, I18n.CODE_EDITOR_STATUS_READ_ONLY);
|
bindDefault(readOnly, I18n.CODE_EDITOR_STATUS_READ_ONLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,12 +184,4 @@ public final class EditorStatusBar extends HBox {
|
|||||||
label.getStyleClass().add("editor-workspace-status-chip");
|
label.getStyleClass().add("editor-workspace-status-chip");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String extensionText(final java.nio.file.Path path) {
|
|
||||||
final var fileName = path.getFileName().toString();
|
|
||||||
final var dot = fileName.lastIndexOf('.');
|
|
||||||
return dot >= 0 && dot < fileName.length() - 1
|
|
||||||
? fileName.substring(dot + 1)
|
|
||||||
: fileName;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,8 @@ import p.studio.vfs.VfsDocumentOpenResult;
|
|||||||
import p.studio.vfs.VfsProjectNode;
|
import p.studio.vfs.VfsProjectNode;
|
||||||
import p.studio.vfs.VfsTextDocument;
|
import p.studio.vfs.VfsTextDocument;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public final class EditorWorkspace extends Workspace {
|
public final class EditorWorkspace extends Workspace {
|
||||||
@ -27,8 +29,10 @@ public final class EditorWorkspace extends Workspace {
|
|||||||
private final EditorHelperPanel helperPanel = new EditorHelperPanel();
|
private final EditorHelperPanel helperPanel = new EditorHelperPanel();
|
||||||
private final EditorStatusBar statusBar = new EditorStatusBar();
|
private final EditorStatusBar statusBar = new EditorStatusBar();
|
||||||
private final EditorTabStrip tabStrip = new EditorTabStrip();
|
private final EditorTabStrip tabStrip = new EditorTabStrip();
|
||||||
|
private final EditorDocumentPresentationRegistry presentationRegistry = new EditorDocumentPresentationRegistry();
|
||||||
private final ProjectDocumentVfs projectDocumentVfs;
|
private final ProjectDocumentVfs projectDocumentVfs;
|
||||||
private final EditorOpenFileSession openFileSession = new EditorOpenFileSession();
|
private final EditorOpenFileSession openFileSession = new EditorOpenFileSession();
|
||||||
|
private final List<String> activePresentationStylesheets = new ArrayList<>();
|
||||||
|
|
||||||
public EditorWorkspace(
|
public EditorWorkspace(
|
||||||
final ProjectReference projectReference,
|
final ProjectReference projectReference,
|
||||||
@ -40,6 +44,7 @@ public final class EditorWorkspace extends Workspace {
|
|||||||
codeArea.setEditable(false);
|
codeArea.setEditable(false);
|
||||||
codeArea.setWrapText(false);
|
codeArea.setWrapText(false);
|
||||||
codeArea.getStyleClass().add("editor-workspace-code-area");
|
codeArea.getStyleClass().add("editor-workspace-code-area");
|
||||||
|
EditorDocumentPresentationStyles.applyToCodeArea(codeArea, presentationRegistry.resolve("text"));
|
||||||
showEditorPlaceholder();
|
showEditorPlaceholder();
|
||||||
navigatorPanel.setRefreshAction(this::refreshNavigator);
|
navigatorPanel.setRefreshAction(this::refreshNavigator);
|
||||||
navigatorPanel.setRevealActiveFileAction(this::revealActiveFileInNavigator);
|
navigatorPanel.setRevealActiveFileAction(this::revealActiveFileInNavigator);
|
||||||
@ -50,7 +55,7 @@ public final class EditorWorkspace extends Workspace {
|
|||||||
});
|
});
|
||||||
|
|
||||||
root.setCenter(buildLayout());
|
root.setCenter(buildLayout());
|
||||||
statusBar.showPlaceholder();
|
statusBar.showPlaceholder(presentationRegistry.resolve("text"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public WorkspaceId workspaceId() { return WorkspaceId.EDITOR; }
|
@Override public WorkspaceId workspaceId() { return WorkspaceId.EDITOR; }
|
||||||
@ -86,6 +91,7 @@ public final class EditorWorkspace extends Workspace {
|
|||||||
openFileSession.open(new EditorOpenFileBuffer(
|
openFileSession.open(new EditorOpenFileBuffer(
|
||||||
textDocument.path(),
|
textDocument.path(),
|
||||||
textDocument.documentName(),
|
textDocument.documentName(),
|
||||||
|
textDocument.typeId(),
|
||||||
textDocument.content(),
|
textDocument.content(),
|
||||||
textDocument.lineSeparator()));
|
textDocument.lineSeparator()));
|
||||||
renderSession();
|
renderSession();
|
||||||
@ -99,13 +105,20 @@ public final class EditorWorkspace extends Workspace {
|
|||||||
activeFile.map(EditorOpenFileBuffer::path).orElse(null));
|
activeFile.map(EditorOpenFileBuffer::path).orElse(null));
|
||||||
if (activeFile.isEmpty()) {
|
if (activeFile.isEmpty()) {
|
||||||
showEditorPlaceholder();
|
showEditorPlaceholder();
|
||||||
statusBar.showPlaceholder();
|
final EditorDocumentPresentation presentation = presentationRegistry.resolve("text");
|
||||||
|
applyPresentationStylesheets(presentation);
|
||||||
|
EditorDocumentPresentationStyles.applyToCodeArea(codeArea, presentation);
|
||||||
|
statusBar.showPlaceholder(presentation);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final var fileBuffer = activeFile.orElseThrow();
|
final var fileBuffer = activeFile.orElseThrow();
|
||||||
|
final EditorDocumentPresentation presentation = presentationRegistry.resolve(fileBuffer.typeId());
|
||||||
|
applyPresentationStylesheets(presentation);
|
||||||
codeArea.replaceText(fileBuffer.content());
|
codeArea.replaceText(fileBuffer.content());
|
||||||
statusBar.showFile(projectReference, fileBuffer);
|
codeArea.setStyleSpans(0, presentation.highlight(fileBuffer.content()));
|
||||||
|
EditorDocumentPresentationStyles.applyToCodeArea(codeArea, presentation);
|
||||||
|
statusBar.showFile(projectReference, fileBuffer, presentation);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void revealActiveFileInNavigator() {
|
private void revealActiveFileInNavigator() {
|
||||||
@ -126,10 +139,22 @@ public final class EditorWorkspace extends Workspace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void showEditorPlaceholder() {
|
private void showEditorPlaceholder() {
|
||||||
codeArea.replaceText("""
|
final EditorDocumentPresentation presentation = presentationRegistry.resolve("text");
|
||||||
|
final String placeholder = """
|
||||||
// Read-only first wave
|
// Read-only first wave
|
||||||
// Open a supported text file from the project navigator.
|
// Open a supported text file from the project navigator.
|
||||||
""");
|
""";
|
||||||
|
applyPresentationStylesheets(presentation);
|
||||||
|
codeArea.replaceText(placeholder);
|
||||||
|
codeArea.setStyleSpans(0, presentation.highlight(placeholder));
|
||||||
|
EditorDocumentPresentationStyles.applyToCodeArea(codeArea, presentation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyPresentationStylesheets(final EditorDocumentPresentation presentation) {
|
||||||
|
root.getStylesheets().removeAll(activePresentationStylesheets);
|
||||||
|
activePresentationStylesheets.clear();
|
||||||
|
activePresentationStylesheets.addAll(presentation.stylesheetUrls());
|
||||||
|
root.getStylesheets().addAll(activePresentationStylesheets);
|
||||||
}
|
}
|
||||||
|
|
||||||
private VBox buildLayout() {
|
private VBox buildLayout() {
|
||||||
|
|||||||
@ -0,0 +1,25 @@
|
|||||||
|
package p.studio.workspaces.editor.syntaxhighlight;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record EditorDocumentHighlightToken(
|
||||||
|
String groupName,
|
||||||
|
Collection<String> styleClasses) {
|
||||||
|
|
||||||
|
public EditorDocumentHighlightToken(final String groupName, final String styleClass) {
|
||||||
|
this(groupName, List.of(styleClass));
|
||||||
|
}
|
||||||
|
|
||||||
|
public EditorDocumentHighlightToken {
|
||||||
|
groupName = Objects.requireNonNull(groupName, "groupName");
|
||||||
|
styleClasses = List.copyOf(Objects.requireNonNull(styleClasses, "styleClasses"));
|
||||||
|
if (groupName.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("groupName cannot be blank");
|
||||||
|
}
|
||||||
|
if (styleClasses.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("styleClasses cannot be empty");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
package p.studio.workspaces.editor.syntaxhighlight;
|
||||||
|
|
||||||
|
import org.fxmisc.richtext.model.StyleSpans;
|
||||||
|
import org.fxmisc.richtext.model.StyleSpansBuilder;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public record EditorDocumentSyntaxHighlighting(
|
||||||
|
Pattern tokenPattern,
|
||||||
|
List<EditorDocumentHighlightToken> tokens) {
|
||||||
|
|
||||||
|
public EditorDocumentSyntaxHighlighting {
|
||||||
|
tokens = List.copyOf(Objects.requireNonNull(tokens, "tokens"));
|
||||||
|
if (tokenPattern == null && !tokens.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("plain text highlighting cannot define tokens");
|
||||||
|
}
|
||||||
|
if (tokenPattern != null && tokens.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("token-based highlighting requires tokens");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EditorDocumentSyntaxHighlighting plainText() {
|
||||||
|
return EditorDocumentSyntaxHighlightingPlainText.PLAIN_TEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EditorDocumentSyntaxHighlighting json() {
|
||||||
|
return EditorDocumentSyntaxHighlightingJson.JSON;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EditorDocumentSyntaxHighlighting bash() {
|
||||||
|
return EditorDocumentSyntaxHighlightingBash.BASH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StyleSpans<Collection<String>> highlight(final String content) {
|
||||||
|
final StyleSpansBuilder<Collection<String>> builder = new StyleSpansBuilder<>();
|
||||||
|
if (tokenPattern == null) {
|
||||||
|
builder.add(Collections.emptyList(), content.length());
|
||||||
|
return builder.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
final Matcher matcher = tokenPattern.matcher(content);
|
||||||
|
int lastMatchEnd = 0;
|
||||||
|
|
||||||
|
while (matcher.find()) {
|
||||||
|
builder.add(Collections.emptyList(), matcher.start() - lastMatchEnd);
|
||||||
|
builder.add(styleClassFor(matcher), matcher.end() - matcher.start());
|
||||||
|
lastMatchEnd = matcher.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.add(Collections.emptyList(), content.length() - lastMatchEnd);
|
||||||
|
return builder.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<String> styleClassFor(final Matcher matcher) {
|
||||||
|
for (final EditorDocumentHighlightToken token : tokens) {
|
||||||
|
if (matcher.group(token.groupName()) != null) {
|
||||||
|
return token.styleClasses();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("matched token without registered style");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package p.studio.workspaces.editor.syntaxhighlight;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class EditorDocumentSyntaxHighlightingBash {
|
||||||
|
public static final EditorDocumentSyntaxHighlighting BASH = new EditorDocumentSyntaxHighlighting(
|
||||||
|
Pattern.compile(
|
||||||
|
"(?<SHEBANG>^#![^\\n]*)"
|
||||||
|
+ "|(?<COMMENT>(?m)(?<!\\S)#[^\\n]*)"
|
||||||
|
+ "|(?<STRING>\"(?:\\\\.|[^\"\\\\])*\"|'(?:\\\\.|[^'\\\\])*')"
|
||||||
|
+ "|(?<VARIABLE>\\$\\{?[A-Za-z_][A-Za-z0-9_]*}?|\\$[0-9@*#?!$-])"
|
||||||
|
+ "|(?<COMMAND>(?m)^[ \\t]*[A-Za-z_./-][A-Za-z0-9_./-]*)"
|
||||||
|
+ "|(?<KEYWORD>\\b(?:if|then|else|elif|fi|for|while|until|do|done|case|esac|in|function|select|time)\\b)"
|
||||||
|
+ "|(?<BUILTIN>\\b(?:echo|printf|read|cd|pwd|export|local|readonly|unset|return|shift|source|trap|exit|test)\\b)"
|
||||||
|
+ "|(?<OPERATOR>\\|\\||&&|;;|<<-?|>>|[|&;<>~=(){}\\[\\]])"),
|
||||||
|
List.of(
|
||||||
|
new EditorDocumentHighlightToken("SHEBANG", "editor-syntax-bash-shebang"),
|
||||||
|
new EditorDocumentHighlightToken("COMMENT", "editor-syntax-bash-comment"),
|
||||||
|
new EditorDocumentHighlightToken("STRING", "editor-syntax-bash-string"),
|
||||||
|
new EditorDocumentHighlightToken("VARIABLE", "editor-syntax-bash-variable"),
|
||||||
|
new EditorDocumentHighlightToken("KEYWORD", "editor-syntax-bash-keyword"),
|
||||||
|
new EditorDocumentHighlightToken("BUILTIN", "editor-syntax-bash-builtin"),
|
||||||
|
new EditorDocumentHighlightToken("COMMAND", "editor-syntax-bash-command"),
|
||||||
|
new EditorDocumentHighlightToken("OPERATOR", "editor-syntax-bash-operator")));
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package p.studio.workspaces.editor.syntaxhighlight;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class EditorDocumentSyntaxHighlightingJson {
|
||||||
|
public static final EditorDocumentSyntaxHighlighting JSON = new EditorDocumentSyntaxHighlighting(
|
||||||
|
Pattern.compile(
|
||||||
|
"(?<KEY>\"(?:\\\\.|[^\"\\\\])*\"(?=\\s*:))"
|
||||||
|
+ "|(?<STRING>\"(?:\\\\.|[^\"\\\\])*\")"
|
||||||
|
+ "|(?<NUMBER>-?\\b\\d+(?:\\.\\d+)?(?:[eE][+-]?\\d+)?\\b)"
|
||||||
|
+ "|(?<BOOLEAN>\\b(?:true|false)\\b)"
|
||||||
|
+ "|(?<NULL>\\bnull\\b)"
|
||||||
|
+ "|(?<PUNCTUATION>[\\{\\}\\[\\],:])"),
|
||||||
|
List.of(
|
||||||
|
new EditorDocumentHighlightToken("KEY", "editor-syntax-json-key"),
|
||||||
|
new EditorDocumentHighlightToken("STRING", "editor-syntax-json-string"),
|
||||||
|
new EditorDocumentHighlightToken("NUMBER", "editor-syntax-json-number"),
|
||||||
|
new EditorDocumentHighlightToken("BOOLEAN", "editor-syntax-json-boolean"),
|
||||||
|
new EditorDocumentHighlightToken("NULL", "editor-syntax-json-null"),
|
||||||
|
new EditorDocumentHighlightToken("PUNCTUATION", "editor-syntax-json-punctuation")));
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package p.studio.workspaces.editor.syntaxhighlight;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class EditorDocumentSyntaxHighlightingPlainText {
|
||||||
|
public static final EditorDocumentSyntaxHighlighting PLAIN_TEXT = new EditorDocumentSyntaxHighlighting(
|
||||||
|
null,
|
||||||
|
List.of());
|
||||||
|
}
|
||||||
@ -536,6 +536,10 @@
|
|||||||
|
|
||||||
.editor-workspace-code-area {
|
.editor-workspace-code-area {
|
||||||
-fx-background-color: #171c22;
|
-fx-background-color: #171c22;
|
||||||
|
-fx-font-family: "JetBrains Mono", "Iosevka", "Cascadia Mono", "IBM Plex Mono", monospace;
|
||||||
|
-fx-font-size: 14px;
|
||||||
|
-fx-highlight-fill: #26405c;
|
||||||
|
-fx-highlight-text-fill: #eef4fb;
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor-workspace-tab-button {
|
.editor-workspace-tab-button {
|
||||||
@ -606,6 +610,28 @@
|
|||||||
-fx-background-color: #171c22;
|
-fx-background-color: #171c22;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area .paragraph-box {
|
||||||
|
-fx-background-color: #171c22;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area .lineno {
|
||||||
|
-fx-background-color: #12161c;
|
||||||
|
-fx-text-fill: #6f7a86;
|
||||||
|
-fx-padding: 0 12 0 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area .text {
|
||||||
|
-fx-fill: #f2f6fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-text .text {
|
||||||
|
-fx-fill: #eef3f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-fe .text {
|
||||||
|
-fx-fill: #f2f6fb;
|
||||||
|
}
|
||||||
|
|
||||||
.workspace-dock-pane {
|
.workspace-dock-pane {
|
||||||
-fx-collapsible: true;
|
-fx-collapsible: true;
|
||||||
-fx-background-color: transparent;
|
-fx-background-color: transparent;
|
||||||
@ -714,6 +740,18 @@
|
|||||||
-fx-max-height: 28;
|
-fx-max-height: 28;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.editor-workspace-status-chip-type-text {
|
||||||
|
-fx-background-color: #11151b;
|
||||||
|
-fx-border-color: #2a313c;
|
||||||
|
-fx-text-fill: #c5d2de;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-status-chip-type-fe {
|
||||||
|
-fx-background-color: #271a35;
|
||||||
|
-fx-border-color: #8d67c7;
|
||||||
|
-fx-text-fill: #f3ecff;
|
||||||
|
}
|
||||||
|
|
||||||
.editor-workspace-status-chip-position {
|
.editor-workspace-status-chip-position {
|
||||||
-fx-min-width: 44;
|
-fx-min-width: 44;
|
||||||
-fx-pref-width: 44;
|
-fx-pref-width: 44;
|
||||||
|
|||||||
@ -0,0 +1,39 @@
|
|||||||
|
.editor-workspace-code-area-type-bash .text {
|
||||||
|
-fx-fill: #edf2f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-bash .lineno {
|
||||||
|
-fx-text-fill: #788595;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-bash .text.editor-syntax-bash-shebang {
|
||||||
|
-fx-fill: #f0ae63;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-bash .text.editor-syntax-bash-comment {
|
||||||
|
-fx-fill: #7d8a98;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-bash .text.editor-syntax-bash-string {
|
||||||
|
-fx-fill: #dcbf88;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-bash .text.editor-syntax-bash-variable {
|
||||||
|
-fx-fill: #8fd4ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-bash .text.editor-syntax-bash-keyword {
|
||||||
|
-fx-fill: #d7a6ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-bash .text.editor-syntax-bash-builtin {
|
||||||
|
-fx-fill: #9fe2a0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-bash .text.editor-syntax-bash-command {
|
||||||
|
-fx-fill: #7fc1ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-bash .text.editor-syntax-bash-operator {
|
||||||
|
-fx-fill: #c7d3de;
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
.editor-workspace-code-area-type-json {
|
||||||
|
-fx-highlight-fill: #204766;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-json .text {
|
||||||
|
-fx-fill: #dff3ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-json .lineno {
|
||||||
|
-fx-text-fill: #7e92a6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-json .text.editor-syntax-json-key {
|
||||||
|
-fx-fill: #8fd4ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-json .text.editor-syntax-json-string {
|
||||||
|
-fx-fill: #d9c48f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-json .text.editor-syntax-json-number {
|
||||||
|
-fx-fill: #9ee39f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-json .text.editor-syntax-json-boolean {
|
||||||
|
-fx-fill: #f3a6d6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-json .text.editor-syntax-json-null {
|
||||||
|
-fx-fill: #c89cff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-code-area-type-json .text.editor-syntax-json-punctuation {
|
||||||
|
-fx-fill: #c8d5e2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-workspace-status-chip-type-json {
|
||||||
|
-fx-background-color: #132433;
|
||||||
|
-fx-border-color: #4e88b8;
|
||||||
|
-fx-text-fill: #e2f4ff;
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
package p.studio.workspaces.editor;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
final class EditorDocumentPresentationRegistryTest {
|
||||||
|
private final EditorDocumentPresentationRegistry registry = new EditorDocumentPresentationRegistry();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resolvesFrontendTypeIdsToFrontendPresentation() {
|
||||||
|
assertEquals("fe", registry.resolve("pbs").styleKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resolvesJsonTypeIdsToJsonPresentation() {
|
||||||
|
final EditorDocumentPresentation presentation = registry.resolve("json");
|
||||||
|
|
||||||
|
assertEquals("json", presentation.styleKey());
|
||||||
|
assertEquals(1, presentation.stylesheetUrls().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resolvesBashTypeIdsToBashPresentation() {
|
||||||
|
final EditorDocumentPresentation presentation = registry.resolve("bash");
|
||||||
|
|
||||||
|
assertEquals("bash", presentation.styleKey());
|
||||||
|
assertEquals(1, presentation.stylesheetUrls().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void fallsBackToTextPresentationForUnknownTypeIds() {
|
||||||
|
assertEquals("text", registry.resolve("markdown").styleKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,7 +11,7 @@ final class EditorOpenFileSessionTest {
|
|||||||
@Test
|
@Test
|
||||||
void openAddsNewFileAndMarksItActive() {
|
void openAddsNewFileAndMarksItActive() {
|
||||||
final var session = new EditorOpenFileSession();
|
final var session = new EditorOpenFileSession();
|
||||||
final var file = new EditorOpenFileBuffer(Path.of("src/main.pbs"), "main.pbs", "fn main(): void\n", "LF");
|
final var file = new EditorOpenFileBuffer(Path.of("src/main.pbs"), "main.pbs", "pbs", "fn main(): void\n", "LF");
|
||||||
|
|
||||||
session.open(file);
|
session.open(file);
|
||||||
|
|
||||||
@ -22,8 +22,8 @@ final class EditorOpenFileSessionTest {
|
|||||||
@Test
|
@Test
|
||||||
void openDoesNotDuplicateExistingTab() {
|
void openDoesNotDuplicateExistingTab() {
|
||||||
final var session = new EditorOpenFileSession();
|
final var session = new EditorOpenFileSession();
|
||||||
final var first = new EditorOpenFileBuffer(Path.of("src/main.pbs"), "main.pbs", "a", "LF");
|
final var first = new EditorOpenFileBuffer(Path.of("src/main.pbs"), "main.pbs", "pbs", "a", "LF");
|
||||||
final var second = new EditorOpenFileBuffer(Path.of("src/main.pbs"), "main.pbs", "b", "LF");
|
final var second = new EditorOpenFileBuffer(Path.of("src/main.pbs"), "main.pbs", "pbs", "b", "LF");
|
||||||
|
|
||||||
session.open(first);
|
session.open(first);
|
||||||
session.open(second);
|
session.open(second);
|
||||||
@ -36,8 +36,8 @@ final class EditorOpenFileSessionTest {
|
|||||||
@Test
|
@Test
|
||||||
void activateSwitchesTheActiveTabWithinTheCurrentSession() {
|
void activateSwitchesTheActiveTabWithinTheCurrentSession() {
|
||||||
final var session = new EditorOpenFileSession();
|
final var session = new EditorOpenFileSession();
|
||||||
final var first = new EditorOpenFileBuffer(Path.of("src/main.pbs"), "main.pbs", "a", "LF");
|
final var first = new EditorOpenFileBuffer(Path.of("src/main.pbs"), "main.pbs", "pbs", "a", "LF");
|
||||||
final var second = new EditorOpenFileBuffer(Path.of("src/other.pbs"), "other.pbs", "b", "LF");
|
final var second = new EditorOpenFileBuffer(Path.of("src/other.pbs"), "other.pbs", "pbs", "b", "LF");
|
||||||
|
|
||||||
session.open(first);
|
session.open(first);
|
||||||
session.open(second);
|
session.open(second);
|
||||||
|
|||||||
@ -0,0 +1,10 @@
|
|||||||
|
package p.studio.vfs;
|
||||||
|
|
||||||
|
public final class VfsDocumentTypeIds {
|
||||||
|
public static final String TEXT = "text";
|
||||||
|
public static final String JSON = "json";
|
||||||
|
public static final String BASH = "bash";
|
||||||
|
|
||||||
|
private VfsDocumentTypeIds() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,12 +6,14 @@ import java.util.Objects;
|
|||||||
public record VfsTextDocument(
|
public record VfsTextDocument(
|
||||||
Path path,
|
Path path,
|
||||||
String documentName,
|
String documentName,
|
||||||
|
String typeId,
|
||||||
String content,
|
String content,
|
||||||
String lineSeparator) implements VfsDocumentOpenResult {
|
String lineSeparator) implements VfsDocumentOpenResult {
|
||||||
|
|
||||||
public VfsTextDocument {
|
public VfsTextDocument {
|
||||||
path = Objects.requireNonNull(path, "path").toAbsolutePath().normalize();
|
path = Objects.requireNonNull(path, "path").toAbsolutePath().normalize();
|
||||||
Objects.requireNonNull(documentName, "documentName");
|
Objects.requireNonNull(documentName, "documentName");
|
||||||
|
Objects.requireNonNull(typeId, "typeId");
|
||||||
Objects.requireNonNull(content, "content");
|
Objects.requireNonNull(content, "content");
|
||||||
Objects.requireNonNull(lineSeparator, "lineSeparator");
|
Objects.requireNonNull(lineSeparator, "lineSeparator");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package p.studio.vfs;
|
package p.studio.vfs;
|
||||||
|
|
||||||
import p.studio.compiler.FrontendRegistryService;
|
import p.studio.compiler.FrontendRegistryService;
|
||||||
|
import p.studio.compiler.models.FrontendSpec;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UncheckedIOException;
|
import java.io.UncheckedIOException;
|
||||||
@ -12,6 +13,7 @@ import java.nio.file.Files;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -98,6 +100,7 @@ final class FilesystemProjectDocumentVfs implements ProjectDocumentVfs {
|
|||||||
return new VfsTextDocument(
|
return new VfsTextDocument(
|
||||||
normalizedPath,
|
normalizedPath,
|
||||||
normalizedPath.getFileName().toString(),
|
normalizedPath.getFileName().toString(),
|
||||||
|
documentTypeId(normalizedPath, content),
|
||||||
content,
|
content,
|
||||||
lineSeparator(content));
|
lineSeparator(content));
|
||||||
}
|
}
|
||||||
@ -118,6 +121,20 @@ final class FilesystemProjectDocumentVfs implements ProjectDocumentVfs {
|
|||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String documentTypeId(final Path path, final String content) {
|
||||||
|
final String extension = extensionOf(path);
|
||||||
|
if ("json".equals(extension) || "ndjson".equals(extension)) {
|
||||||
|
return VfsDocumentTypeIds.JSON;
|
||||||
|
}
|
||||||
|
if (isBashDocument(path, extension, content)) {
|
||||||
|
return VfsDocumentTypeIds.BASH;
|
||||||
|
}
|
||||||
|
if (isFrontendSourceDocument(extension)) {
|
||||||
|
return projectContext.languageId();
|
||||||
|
}
|
||||||
|
return VfsDocumentTypeIds.TEXT;
|
||||||
|
}
|
||||||
|
|
||||||
private VfsProjectNode buildNode(
|
private VfsProjectNode buildNode(
|
||||||
final Path path,
|
final Path path,
|
||||||
final String displayName,
|
final String displayName,
|
||||||
@ -194,6 +211,39 @@ final class FilesystemProjectDocumentVfs implements ProjectDocumentVfs {
|
|||||||
return Objects.requireNonNull(path, "path").toAbsolutePath().normalize();
|
return Objects.requireNonNull(path, "path").toAbsolutePath().normalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isFrontendSourceDocument(final String extension) {
|
||||||
|
if (extension.isBlank()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return FrontendRegistryService.getFrontendSpec(projectContext.languageId())
|
||||||
|
.map(FrontendSpec::getAllowedExtensions)
|
||||||
|
.stream()
|
||||||
|
.flatMap(allowedExtensions -> allowedExtensions.stream())
|
||||||
|
.anyMatch(allowedExtension -> allowedExtension.equalsIgnoreCase(extension));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isBashDocument(final Path path, final String extension, final String content) {
|
||||||
|
if (Set.of("sh", "bash", "bashrc", "bash_profile").contains(extension)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
final String fileName = path.getFileName().toString().toLowerCase(Locale.ROOT);
|
||||||
|
if (".bashrc".equals(fileName) || ".bash_profile".equals(fileName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
final int firstLineBreak = content.indexOf('\n');
|
||||||
|
final String firstLine = firstLineBreak >= 0 ? content.substring(0, firstLineBreak) : content;
|
||||||
|
return firstLine.startsWith("#!") && (firstLine.contains("bash") || firstLine.contains("/sh"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extensionOf(final Path path) {
|
||||||
|
final String fileName = path.getFileName().toString();
|
||||||
|
final int dot = fileName.lastIndexOf('.');
|
||||||
|
if (dot < 0 || dot == fileName.length() - 1) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return fileName.substring(dot + 1).toLowerCase(Locale.ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean containsNulByte(final byte[] bytes) {
|
private boolean containsNulByte(final byte[] bytes) {
|
||||||
for (final byte value : bytes) {
|
for (final byte value : bytes) {
|
||||||
if (value == 0) {
|
if (value == 0) {
|
||||||
|
|||||||
@ -43,10 +43,50 @@ final class FilesystemProjectDocumentVfsTest {
|
|||||||
final VfsTextDocument document = assertInstanceOf(VfsTextDocument.class, result);
|
final VfsTextDocument document = assertInstanceOf(VfsTextDocument.class, result);
|
||||||
|
|
||||||
assertEquals("main.pbs", document.documentName());
|
assertEquals("main.pbs", document.documentName());
|
||||||
|
assertEquals("pbs", document.typeId());
|
||||||
assertEquals("LF", document.lineSeparator());
|
assertEquals("LF", document.lineSeparator());
|
||||||
assertTrue(document.content().contains("fn main()"));
|
assertTrue(document.content().contains("fn main()"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void openDocumentClassifiesJsonFilesWithJsonTypeIdentifier() throws Exception {
|
||||||
|
final Path file = tempDir.resolve("prometeu.json");
|
||||||
|
Files.writeString(file, "{\n \"name\": \"Example\"\n}\n");
|
||||||
|
|
||||||
|
final ProjectDocumentVfs vfs = new FilesystemProjectDocumentVfsFactory().open(projectContext());
|
||||||
|
|
||||||
|
final VfsDocumentOpenResult result = vfs.openDocument(file);
|
||||||
|
final VfsTextDocument document = assertInstanceOf(VfsTextDocument.class, result);
|
||||||
|
|
||||||
|
assertEquals(VfsDocumentTypeIds.JSON, document.typeId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void openDocumentClassifiesNdjsonFilesWithJsonTypeIdentifier() throws Exception {
|
||||||
|
final Path file = tempDir.resolve("events.ndjson");
|
||||||
|
Files.writeString(file, "{\"event\":\"start\"}\n{\"event\":\"done\"}\n");
|
||||||
|
|
||||||
|
final ProjectDocumentVfs vfs = new FilesystemProjectDocumentVfsFactory().open(projectContext());
|
||||||
|
|
||||||
|
final VfsDocumentOpenResult result = vfs.openDocument(file);
|
||||||
|
final VfsTextDocument document = assertInstanceOf(VfsTextDocument.class, result);
|
||||||
|
|
||||||
|
assertEquals(VfsDocumentTypeIds.JSON, document.typeId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void openDocumentClassifiesBashScriptsWithBashTypeIdentifier() throws Exception {
|
||||||
|
final Path file = tempDir.resolve("script.sh");
|
||||||
|
Files.writeString(file, "#!/usr/bin/env bash\nprintf \"hi\"\n");
|
||||||
|
|
||||||
|
final ProjectDocumentVfs vfs = new FilesystemProjectDocumentVfsFactory().open(projectContext());
|
||||||
|
|
||||||
|
final VfsDocumentOpenResult result = vfs.openDocument(file);
|
||||||
|
final VfsTextDocument document = assertInstanceOf(VfsTextDocument.class, result);
|
||||||
|
|
||||||
|
assertEquals(VfsDocumentTypeIds.BASH, document.typeId());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void openDocumentRejectsBinaryLikeFiles() throws Exception {
|
void openDocumentRejectsBinaryLikeFiles() throws Exception {
|
||||||
final Path file = tempDir.resolve("sprite.bin");
|
final Path file = tempDir.resolve("sprite.bin");
|
||||||
|
|||||||
@ -208,6 +208,126 @@
|
|||||||
"message" : "Asset scan started",
|
"message" : "Asset scan started",
|
||||||
"severity" : "INFO",
|
"severity" : "INFO",
|
||||||
"sticky" : false
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "0 assets loaded",
|
||||||
|
"severity" : "SUCCESS",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Asset scan started",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "0 assets loaded",
|
||||||
|
"severity" : "SUCCESS",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Asset scan started",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "0 assets loaded",
|
||||||
|
"severity" : "SUCCESS",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Asset scan started",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "0 assets loaded",
|
||||||
|
"severity" : "SUCCESS",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Asset scan started",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "0 assets loaded",
|
||||||
|
"severity" : "SUCCESS",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Asset scan started",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "0 assets loaded",
|
||||||
|
"severity" : "SUCCESS",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Asset scan started",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "0 assets loaded",
|
||||||
|
"severity" : "SUCCESS",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Asset scan started",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "0 assets loaded",
|
||||||
|
"severity" : "SUCCESS",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Asset scan started",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "0 assets loaded",
|
||||||
|
"severity" : "SUCCESS",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Asset scan started",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "0 assets loaded",
|
||||||
|
"severity" : "SUCCESS",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Asset scan started",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "0 assets loaded",
|
||||||
|
"severity" : "SUCCESS",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Asset scan started",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "0 assets loaded",
|
||||||
|
"severity" : "SUCCESS",
|
||||||
|
"sticky" : false
|
||||||
|
}, {
|
||||||
|
"source" : "Assets",
|
||||||
|
"message" : "Asset scan started",
|
||||||
|
"severity" : "INFO",
|
||||||
|
"sticky" : false
|
||||||
}, {
|
}, {
|
||||||
"source" : "Studio",
|
"source" : "Studio",
|
||||||
"message" : "Project ready",
|
"message" : "Project ready",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user