Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
d48bc00
work on symbol indexer/lookup
NateIsStalling Jul 11, 2026
5edfa4a
lexer for symbol lookup and highlighting
NateIsStalling May 22, 2026
67f4fac
function lookup and lexing
NateIsStalling May 22, 2026
f41586e
work on grammar/scope handling
NateIsStalling May 22, 2026
36881b2
scope grammar fixes
NateIsStalling May 22, 2026
4b3ee6f
code folding
NateIsStalling May 22, 2026
698f5d1
filtering function ref ui
NateIsStalling May 22, 2026
99812a6
work on file browser
NateIsStalling May 22, 2026
75787b0
build fix
NateIsStalling Jul 12, 2026
c8e8d2d
run spotless
NateIsStalling Jul 12, 2026
c4f77bd
work on file viewers
NateIsStalling Jul 12, 2026
ea542ce
build fixes; refactoring content viewer structure
NateIsStalling Jul 12, 2026
dcbc47c
moving antlr into the language adapter project - added spotless
NateIsStalling Jul 12, 2026
29d5dff
refactoring editor panels into separate components from the mainwindow
NateIsStalling Jul 12, 2026
6f2d2ec
project builds
NateIsStalling Jul 12, 2026
21e3503
adjusting splitpanes around new tabs
NateIsStalling Jul 12, 2026
195c749
resolve language service todos
NateIsStalling Jul 14, 2026
624dfa9
work on card panel layouts and buttons
NateIsStalling Jul 14, 2026
222f7a6
reference panel cleanup
NateIsStalling Jul 14, 2026
00c0a5b
implement bookmarks panel
NateIsStalling Jul 14, 2026
a70544b
fix panel collapsing state
NateIsStalling Jul 14, 2026
24c980a
update icons
NateIsStalling Jul 14, 2026
fc33ab2
work on docs panel
NateIsStalling Jul 14, 2026
833e444
docs icons
NateIsStalling Jul 14, 2026
7d1f1d2
work on cleaning up file openers + docs panel + markdown support
NateIsStalling Jul 16, 2026
04e8b24
implement markdown editor options
NateIsStalling Jul 16, 2026
25d59f2
fix exception with tabs
NateIsStalling Jul 16, 2026
52681c8
Add Programmer's Guide to docs
NateIsStalling Jul 16, 2026
1462c3e
move samples and docs
NateIsStalling Jul 16, 2026
5567cc0
work on docs and sample templates
NateIsStalling Jul 16, 2026
4ada2fa
work on docs panels
NateIsStalling Jul 16, 2026
26b6214
runnable tab management and default tab names
NateIsStalling Jul 18, 2026
238b44b
work on empty panel layouts
NateIsStalling Jul 18, 2026
61d4ef9
split panel sizing adjustments
NateIsStalling Jul 19, 2026
b5995c5
update docs links
NateIsStalling Jul 20, 2026
ada697a
Merge commit 'ab0d96bcca0c1040f39ab3853b789fe900078e79' into editor-ui
NateIsStalling Jul 20, 2026
0178951
Merge commit 'f13dd350dbfd93d5852391b652eda61128e2286e' into editor-ui
NateIsStalling Jul 20, 2026
24326e8
Merge commit 'ce0b80f354841761446f6b66db495fab1224655a' into editor-ui
NateIsStalling Jul 27, 2026
73185d2
resolved todo
NateIsStalling Jul 27, 2026
241d3b3
cleanup unused layout component
NateIsStalling Jul 27, 2026
24c00dc
feature flag for file explorer
NateIsStalling Jul 28, 2026
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.basic4gl.desktop.spi;

public record BookmarkInfo(String filePath, String fileName, int lineNumber, String lineText) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.basic4gl.desktop.spi;

public interface DebugController {
void actionPlayPause();

void actionStep();

void actionStepInto();

void actionStepOutOf();
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ public interface DialogService {
// JComponent createContent(DialogContext context);
// Boolean getResult();
// default boolean validate() { return true; }
public boolean showDialog(PluginContext context);
public void showDialog(String message);

String showInputDialog(String message, String title, String initialValue);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.basic4gl.desktop.spi;

import java.io.File;
import java.util.List;

public interface EditorCommandsService {
void openFileWithPreferredViewer(File file);

public String collectAllSourceText();

void actionOpenFolder();

void selectNextBookmark();

void selectPreviousBookmark();

void toggleBookmark();

List<BookmarkInfo> listBookmarks();

void goToBookmark(String filePath, int lineNumber);

void setWorkspaceDirectory(File selectedFile);

void insertText(String text, int caretOffset);
}
11 changes: 11 additions & 0 deletions app-spi/src/main/java/com/basic4gl/desktop/spi/PluginContext.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
package com.basic4gl.desktop.spi;

import com.basic4gl.desktop.spi.content.ContentService;

public interface PluginContext {
// CommandRegistry commands();
// MenuRegistry menus();
// ToolWindowRegistry toolWindows();

EditorCommandsService commands();

DebugController debugger();

DialogService dialogs();

MenuService menus();

ContentService content();
// ProjectService projects();
// EditorService editors();
FileOpener files();
Expand All @@ -15,6 +24,8 @@ public interface PluginContext {

String currentDirectory();

EditorPlugin currentEditor();

String getLibraryPath();

SourceFileService[] fileServices();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.basic4gl.desktop.spi.content;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

public final class ClasspathContentSource implements ContentSource {

private final ClassLoader classLoader;
private final String rootPrefix;
private final List<ContentResource> resources;

public ClasspathContentSource(ClassLoader classLoader, String rootPrefix, String indexResourcePath)
throws IOException {
this.classLoader = Objects.requireNonNull(classLoader, "classLoader");
this.rootPrefix = normalizePrefix(rootPrefix);
String indexPath = ContentPaths.normalize(indexResourcePath);
try (InputStream input = classLoader.getResourceAsStream(indexPath)) {
if (input == null) {
throw new FileNotFoundException("Classpath content index not found: " + indexPath);
}
this.resources = new String(input.readAllBytes(), StandardCharsets.UTF_8)
.lines()
.map(String::trim)
.filter(line -> !line.isBlank())
.filter(line -> !line.startsWith("#"))
.map(ContentPaths::normalize)
.map(ContentResource::new)
.sorted((left, right) -> left.path().compareToIgnoreCase(right.path()))
.toList();
}
}

@Override
public InputStream open(String normalizedPath) throws IOException {
String safePath = ContentPaths.normalize(normalizedPath);
InputStream input = classLoader.getResourceAsStream(rootPrefix + safePath);
if (input == null) {
throw new FileNotFoundException("Classpath content resource not found: " + safePath);
}
return input;
}

@Override
public Collection<ContentResource> resources() {
return resources;
}

private static String normalizePrefix(String rootPrefix) {
if (rootPrefix == null || rootPrefix.isBlank()) {
return "";
}
return ContentPaths.normalize(rootPrefix) + "/";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.basic4gl.desktop.spi.content;

import java.io.File;

@Deprecated
public class Content {
private String name;
private ContentMetadata metadata;

private File file;
private boolean readonly;

public Content(String name, ContentMetadata metadata, File file, boolean readonly) {
this.name = name;
this.metadata = metadata != null ? metadata : new ContentMetadata();
this.file = file;
this.readonly = readonly;
}

public String getName() {
return name;
}

public ContentMetadata getMetadata() {
return metadata;
}

public File getFile() {
return file;
}

public boolean isReadonly() {
return readonly;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.basic4gl.desktop.spi.content;

import java.util.Objects;

public record ContentDocument(String mediaType, ContentSource source, String entryPath) {

public ContentDocument {
mediaType = ContentValidation.requireNonBlank(mediaType, "mediaType");
source = Objects.requireNonNull(source, "source");
entryPath = ContentValidation.requireNonBlank(entryPath, "entryPath");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.basic4gl.desktop.spi.content;

@Deprecated
public class ContentMetadata {
private String category;
private String description;
private String[] tags;
private String author;
private String version;

public ContentMetadata() {
tags = new String[0];
}

public ContentMetadata(String category, String description, String[] tags, String author, String version) {
this.category = category;
this.description = description;
this.tags = tags;
this.author = author;
this.version = version;
}

public String getCategory() {
return category;
}

public void setCategory(String category) {
this.category = category;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public String[] getTags() {
return tags;
}

public void setTags(String[] tags) {
this.tags = tags;
}

public String getAuthor() {
return author;
}

public void setAuthor(String author) {
this.author = author;
}

public String getVersion() {
return version;
}

public void setVersion(String version) {
this.version = version;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.basic4gl.desktop.spi.content;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Locale;

public final class ContentPaths {

private ContentPaths() {}

public static String normalize(String path) {
rejectUnsafePath(path);
String normalizedSeparators = path.replace('\\', '/');
Deque<String> segments = new ArrayDeque<>();
for (String segment : normalizedSeparators.split("/+")) {
if (segment.isEmpty() || ".".equals(segment)) {
continue;
}
if ("..".equals(segment)) {
if (segments.isEmpty()) {
throw new IllegalArgumentException("Path escapes the content root: " + path);
}
segments.removeLast();
continue;
}
segments.addLast(segment);
}
if (segments.isEmpty()) {
throw new IllegalArgumentException("Path must not resolve to the content root");
}
return String.join("/", segments);
}

public static String resolve(String currentDocumentPath, String relativeTarget) {
String currentPath = normalize(currentDocumentPath);
rejectUnsafePath(relativeTarget);
String parent = "";
int lastSlash = currentPath.lastIndexOf('/');
if (lastSlash >= 0) {
parent = currentPath.substring(0, lastSlash);
}
String combined = parent.isBlank() ? relativeTarget : parent + "/" + relativeTarget;
return normalize(combined);
}

private static void rejectUnsafePath(String path) {
if (path == null || path.isBlank()) {
throw new IllegalArgumentException("Path must not be null or blank");
}
String trimmed = path.trim();
String slashPath = trimmed.replace('\\', '/');
if (slashPath.startsWith("/") || slashPath.startsWith("//")) {
throw new IllegalArgumentException("Absolute paths are not allowed: " + path);
}
if (slashPath.matches("^[A-Za-z]:($|/.*)")) {
throw new IllegalArgumentException("Windows absolute paths are not allowed: " + path);
}
String lower = slashPath.toLowerCase(Locale.ROOT);
if (lower.matches("^[a-z][a-z0-9+.-]*:.*")) {
throw new IllegalArgumentException("URI schemes are not allowed: " + path);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.basic4gl.desktop.spi.content;

public interface ContentRegistration extends AutoCloseable {

@Override
void close();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.basic4gl.desktop.spi.content;

public record ContentResource(String path) {

public ContentResource {
path = ContentValidation.requireNonBlank(path, "path");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.basic4gl.desktop.spi.content;

public interface ContentService {
ContentRegistration registerDocumentProvider(DocumentProvider provider);

ContentRegistration registerTemplateProvider(TemplateProvider provider);

@Deprecated
default void registerTemplate(Template template) {
registerTemplateProvider(new LegacyTemplateProvider(template));
}

@Deprecated
default void registerDocument(Content content) {
registerDocumentProvider(new LegacyContentDocumentProvider(content));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.basic4gl.desktop.spi.content;

import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;

public interface ContentSource {

/**
* Opens a normalized path relative to this source's root.
*/
InputStream open(String normalizedPath) throws IOException;

/**
* Returns all files belonging to this content bundle.
*/
Collection<ContentResource> resources() throws IOException;
}
Loading
Loading