Conversation
…lates before processing
…oved some function out of Processor
| [ | ||
| { "path": "demo/**/demo.java", "cmd": "java --enable-preview -Dsun.stdout.encoding=UTF-8" }, | ||
| { "path": "demo/sub1/demo.bat", "cmd": "" } | ||
| ] No newline at end of file |
There was a problem hiding this comment.
Dokumentation: Wie funktioniert die Überwachung? Welche Dateien müssen wo liegen, um eine Ausführung zu triggern?
src/main/java/lvp/Main.java
Outdated
| case "--watch-filter", "-w": | ||
| watchFilter = value.isBlank() ? Optional.empty() : Optional.of(value); | ||
| break; | ||
| case "--source-only", "-s": |
There was a problem hiding this comment.
Begriff "source" geglückt? unklar, ob änderungswürdig
| private static void handleServerCommands(String command) { | ||
| String[] parts = command.split(" ", 2); | ||
| if (command.isBlank() || parts.length == 0) { | ||
| System.out.println("No command entered. Type '/help' for available commands."); | ||
| return; | ||
| } | ||
|
|
||
| switch (parts[0].toLowerCase()) { | ||
| case "exit" -> { | ||
| System.out.println("Exiting Live View Programming..."); | ||
| System.exit(0); | ||
| } | ||
| case "log" -> { | ||
| if (parts.length < 2) { | ||
| System.out.println("Usage: /log <level>"); | ||
| return; | ||
| } | ||
| LogLevel level = LogLevel.fromString(parts[1]); | ||
| if (level == null) { | ||
| System.out.println("Invalid log level."); | ||
| } else { | ||
| Logger.setLogLevel(level); | ||
| System.out.println("Log level set to: " + level); | ||
| } | ||
| } | ||
| case "help" -> System.out.println("Available commands: /exit, /help, /log"); | ||
| default -> System.out.println("Unknown command: " + command); | ||
| } | ||
| } |
There was a problem hiding this comment.
Zukünftige Architektur: Über die von LVP gestartete Konsole können ganz "normal" beliebige Befehle abgesetzt werden; ein log sollte als Dienst ganz normal von jedem Service aus verändert werden können. Ebenso wie über die Konsole auch Markdown etc. abgesetzt werden können sollte.
| if (!isLatestRelease()) { | ||
| System.out.println("Warning: You are not using the latest release of Live View Programming. Please visit https://github.com/denkspuren/LiveViewProgramming/releases"); | ||
| } |
There was a problem hiding this comment.
Idee: Weil das aus gemachter Erfahrung keiner liest: Vielleicht ein Laufband im Browser "There is a new version available"
| ProcessBuilder pb = new ProcessBuilder(isWindows ? new String[]{"cmd.exe", "/c", source.cmd(), source.path().toString()} : new String[]{"sh", "-c", source.cmd(), source.path().toString()}) | ||
| .redirectErrorStream(true); |
There was a problem hiding this comment.
Kann man das nicht auch betriebssystemunabhängig halten, so dass nicht zwischen cmd und sh unterschieden werden muss? Dann kümmert sich Java darum.
| private void run(Source source) { | ||
| processor.init(source.id()); |
There was a problem hiding this comment.
Könnte in den Kommandoprozessor wandern.
| public class FileWatcher { | ||
| private WatchService watcher; |
There was a problem hiding this comment.
Status quo: FileWatcher startet Programme und nicht der Kommandoprozessor.
Zukünftig:
Der FileWatcher ist ein ganz "normaler" interner Service, der Dateien auf Änderungen überwacht und den KommandoProzessor über ein Ereignis auf eine Änderung hinweist. Der KommandoProzessor entscheidet daraufhin, was gemacht wird.
| private Map<Path, Instant> lastModified = new ConcurrentHashMap<>(); | ||
| private boolean isRunning = true; | ||
| Path dir; | ||
| String fileNamePattern; | ||
|
|
||
| public FileWatcher(Path dir, String fileNamePattern, Server server) throws IOException{ | ||
| this.dir = dir; | ||
| this.fileNamePattern = fileNamePattern; | ||
|
|
||
| private static final Duration DEBOUNCE_DURATION = Duration.ofMillis(500); |
There was a problem hiding this comment.
Die Zeitschwelle ist eine interne Buchführung des FileWatchers, um Änderungen erst nach dem Ablauf einer gewissen Zeit wieder dem Kommandoprozessor zu melden.
| void process(Process process, String sourceId) { | ||
| try(BufferedReader reader = new BufferedReader( | ||
| new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) { | ||
|
|
||
| InstructionParser.parse(reader.lines()).gather(Gatherers.fold(() -> "", (prev, curr) -> | ||
| switch (curr) { | ||
| case Command cmd -> processCommands(cmd, sourceId, process); | ||
| case Pipe pipe -> processPipe(pipe, prev, sourceId, process); | ||
| case Register register -> processRegister(register); | ||
| case Unknown unknown -> processUnknown(unknown, sourceId); | ||
| default -> null; | ||
| })).forEachOrdered(_->{}); | ||
| } | ||
| catch (Exception e) { | ||
| Logger.logError("Error reading process output: " + e.getMessage(), e); | ||
| } | ||
| } |
| public class Text { | ||
| private Text() {} | ||
| static Map<String, String> memory = new ConcurrentHashMap<>(); |
There was a problem hiding this comment.
Überarbeitung des Aufbaus von Kommando-Pipes
Pipes sind Textverarbeitungsfolgen aus Quellen und Wandlern, wobei Wandler Seiteneffekte haben können (die manchmal wesentlich und entscheidend sind), aber nicht haben müssen.
Quellen beginnen/initiieren eine Pipe-Verarbeitung. Quellen, die in einer Pipe vorkommen, ignorieren den durchgereichten Text und ersetzen ihn durch den Text der Quelle.
Quellen stehen in einer Textzeile immer am Anfang. Soll der Textanfang ausnahmsweise nicht als Kommando interpretiert werden, kann eventuell Abhilfe durch ein führendes Leerzeichen geschaffen werden (Markdown würde das ignorieren). Oder der Befehl wird per Alias umbenannt, so dass der Textanfang keine Kommandoausführung triggert.
Wandler können Seiteneffekte haben (z.B. Text speichern) und wandeln den Text um. Wenn nur der Seiteneffekt relevant ist, wird der Text identisch durchgereicht.
Wandler stehen in einer Textzeile immer an Anfang, ihnen ist jedoch ein Pipe-Symbol mit folgendem Leerzeichen "| " vorangestellt.
Beispiel: "Hallo Welt!" beginnt die Pipe mit dem Text. Save 23 reicht den Text durch, speichert in als Seiteneffekt im Register 23 ab. Markdown liefert den Text als Seiteneffekt zum Rendering an den Browser und reicht ihn ansonsten weiter.
"""
Hallo Welt!
| Save: 23
| Markdown
"""Im folgenden Fall steht Guten Tag als nächste Quelle da, die eine neue Pipe eröffnet und die bisherige beendet.
"""
Hallo Welt!
| Save: 23
| Markdown
Guten Tag
| Markdown
"""Für den Umgang mit Text, der Platzhalter enthält, gibt es das Filler-Kommando, das konfiguriert wird mit dem Fülltext und bei der Bearbeitung den Fülltext im ersten Platzhalter des übergebenen Textes einfügt. Unterwegs wird der teilausgefüllte Text in Register 23 gespeichert.
"""
Hallo ${0}${1}
| Filler: World
| Save: 23
| Filler: !
| Markdown
"""Das teilausgefüllte Template wird mit Load geladen (Load ist eine Quelle) und der zweite Platzhalter mit einem andere Filler ausgefüllt.
"""
Load: 23
| Filler: ?
| Markdown
"""Back to the roots: Die Turtle ist kommandogetrieben. Wer einen Wrapper braucht, bitte selber machen! Damit wird die Turtle zu einem Kommando, das durch einen internen Service realisiert wird. Die Identität bindet die Turtle auch optisch eine eine View, die die Illusion der Identität visuell unterstützt.
IO.println("alias ':::| turtle' 'turtle:'");
IO.println("turtle: new 23");
int step = 10;
int degrees = 30;
for (int i = 1; i <= 6; i = i + 1) {
IO.println("turtle: forward " + step);
IO.println("turtle: left " + degrees);
}
IO.println("turtle: build --with-timeline");
IO.println("alias --reset ':::| turtle')";Idee: Auch ein Prefix: <prefix> könnte global, ähnlich einem Alias, alle Befehle mit einem Prefix versehen.
Register
Register können auch als Argumente in Befehlen verwendet werden, dem Registernamen ist ein $ voranzustellen. Der Kommandoprozessor ersetzt die Registerreferenz mit dem Text des Registers vor der Ausführung des Kommandos.
Beispiel:
"""
Register: JShell "jshell --enable-preview -R-ea` --STDOUT --STDIN --interaction-counter=1"
Start-Process: jshell --enable-preview -R-ea
2 + 3
| Save: Expr
| Jshell
| Save: Result
Das Ergebnis von ${0} ist ${1}.
| Filler: $Expr
| Filler: $Result
| Markdown
""""""
Register: JShell "jshell -R-ea" --STDIN=$IN --STDOUT=$OUT --STDERR=$ERR
Register: JShell "jshell -R-ea" --STDIN=$IN --STDOUT=$OUT --STDERR=$ERR
Run: JShell // §Processid123
| Store $JShell1
Snippet
| $JShell1
| Markdown
Snippet
| JShell
| Store: OUT
Snippet
| JShell
| Cond: !$ERR
| Markdown
Load $ERR
| Markdown: --overlay
Load $OUT
"""Beispiel für einen konfigurierbaren Basic-Calculator (bc). Die Übergabe aus der Pipe wird standardmäßig über den Speicher $IN bereitgestellt.
In dem rechts von einem Kommando stehenden Text (der das Kommando konfiguriert) werden Speicherreferenzen (erkennbar am $) durch ihren Textwert ersetzt.
"""
Register: Calc "bc -e $IN" --STDOUT
7 + 5 | Calc
"""Offene Fragen
Wie geht der Kommandoprocessor mit Ereignissen um, die etwas antriggern.
No description provided.