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
506 changes: 506 additions & 0 deletions .github/workflows/test-dap.yml

Large diffs are not rendered by default.

11 changes: 10 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,13 @@ build
.metals
.bloop
generated/
test/scratch
test/scratch
/profiling/output
/test-output
/profiling/test-output
.vscode/settings.json
.claude/
*.md
!README.md
build-output.txt
test-output.txt
4 changes: 0 additions & 4 deletions .vscode/settings.json

This file was deleted.

10 changes: 10 additions & 0 deletions extension/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Manifest-Version: 1.0
id: FA79A831-7D30-4D8A-B7F300DECEB00001
name: "Luceedebug"
symbolic-name: "luceedebug"
description: "Native CFML debugger for VS Code - no Java agent required"
version: "3.0.0-BETA"
lucee-core-version: "7.1.0.6"
start-bundles: true
release-type: server
startup-hook: [{"class": "luceedebug.extension.ExtensionActivator", "bundle-name": "luceedebug-osgi", "bundle-version": "3.0.0.0-BETA"}]
28 changes: 26 additions & 2 deletions luceedebug/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,14 @@ tasks.jar {
"Premain-Class" to "luceedebug.Agent",
"Can-Redefine-Classes" to "true",
"Bundle-SymbolicName" to "luceedebug-osgi",
"Bundle-Version" to "2.0.1.1",
"Bundle-Version" to "3.0.0.0-BETA",
"Export-Package" to "luceedebug.*"
)
)
}
}

val luceedebugVersion = "2.0.15"
val luceedebugVersion = "3.0.0-BETA"
val libfile = "luceedebug-" + luceedebugVersion + ".jar"

// TODO: this should, but does not currently, participate in the `clean` task, so the generated file sticks around after invoking `clean`.
Expand All @@ -117,3 +117,27 @@ tasks.shadowJar {
relocationPrefix = "luceedebug_shadow"
archiveFileName.set(libfile)
}

// Extension packaging task - creates .lex file for Lucee extension deployment
val extensionVersion = "3.0.0"
val extensionFile = "luceedebug-extension-${extensionVersion}.lex"

tasks.register<Zip>("buildExtension") {
dependsOn("shadowJar")
archiveFileName.set(extensionFile)
destinationDirectory.set(file("${layout.buildDirectory.get()}/extension"))

// Include the shadow JAR as the main library
from(tasks.shadowJar.get().outputs) {
into("jars")
}

// Include the extension manifest
from("${rootProject.projectDir}/extension/META-INF") {
into("META-INF")
}

doLast {
println("Built extension: ${destinationDirectory.get()}/${extensionFile}")
}
}
17 changes: 15 additions & 2 deletions luceedebug/src/main/java/luceedebug/Agent.java
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,23 @@ private static Map<String, Integer> linearizedCoreInjectClasses() {
result.put("luceedebug.coreinject.frame.Frame$FrameContext", 1);
result.put("luceedebug.coreinject.frame.Frame$FrameContext$SupplierOrNull", 1);
result.put("luceedebug.coreinject.frame.DummyFrame", 1);

result.put("luceedebug.coreinject.frame.NativeDebugFrame", 1);

// Native debugger classes - not used in agent mode but need to be in the map
result.put("luceedebug.coreinject.NativeLuceeVm", 0);
result.put("luceedebug.coreinject.NativeLuceeVm$1", 0);
result.put("luceedebug.coreinject.NativeLuceeVm$2", 0);
result.put("luceedebug.coreinject.NativeLuceeVm$3", 0);
result.put("luceedebug.coreinject.NativeDebuggerListener", 0);
result.put("luceedebug.coreinject.NativeDebuggerListener$1", 0);
result.put("luceedebug.coreinject.NativeDebuggerListener$CachedExecutableLines", 0);
result.put("luceedebug.coreinject.NativeDebuggerListener$StepState", 0);
result.put("luceedebug.coreinject.NativeDebuggerListener$SuspendLocation", 0);
result.put("luceedebug.coreinject.StepMode", 0);

return result;
}

public static Comparator<ClassInjection> comparator() {
final Map<String, Integer> ordering = linearizedCoreInjectClasses();
return Comparator.comparing(injection -> {
Expand Down
49 changes: 47 additions & 2 deletions luceedebug/src/main/java/luceedebug/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,23 @@ public class Config {
// but for now it's configurable
private boolean stepIntoUdfDefaultValueInitFrames_ = false;

Config(boolean fsIsCaseSensitive) {
/**
* Static cache of filesystem case sensitivity.
* Set once at startup when Config is instantiated.
* Used by canonicalizeFileName() to skip lowercase on case-sensitive filesystems.
*/
private static volatile boolean staticFsIsCaseSensitive = false;

/**
* Base path prefix for shortening paths in log output.
* Set from pathTransforms when DAP client attaches.
*/
private static volatile String basePath = null;

public Config(boolean fsIsCaseSensitive) {
this.fsIsCaseSensitive_ = fsIsCaseSensitive;
// Cache for static access
staticFsIsCaseSensitive = fsIsCaseSensitive;
}

public boolean getStepIntoUdfDefaultValueInitFrames() {
Expand Down Expand Up @@ -51,7 +66,37 @@ public boolean getFsIsCaseSensitive() {
}

public static String canonicalizeFileName(String s) {
return s.replaceAll("[\\\\/]+", "/").toLowerCase();
// Normalize slashes (always needed)
String normalized = s.replaceAll("[\\\\/]+", "/");
// Only lowercase on case-insensitive filesystems (Windows)
return staticFsIsCaseSensitive ? normalized : normalized.toLowerCase();
}

/**
* Set the base path for shortening paths in log output.
*/
public static void setBasePath(String path) {
basePath = path != null ? canonicalizeFileName(path) : null;
}

/**
* Shorten a path for display by removing the base path prefix.
* Returns the relative path (with leading /) if it starts with basePath, otherwise the full path.
*/
public static String shortenPath(String path) {
if (basePath == null || path == null) {
return path;
}
String canon = canonicalizeFileName(path);
if (canon.startsWith(basePath)) {
String relative = canon.substring(basePath.length());
// Ensure leading slash
if (!relative.startsWith("/")) {
relative = "/" + relative;
}
return relative;
}
return path;
}

}
Loading
Loading