"`, js)
}
fmt.Fprintf(w, " %s %s --> %s\n", nid, label, to)
diff --git a/tools/mermaid_test.go b/tools/mermaid_test.go
index 1f06680..62ed465 100644
--- a/tools/mermaid_test.go
+++ b/tools/mermaid_test.go
@@ -1,3 +1,15 @@
+/* Copyright 2018 Comcast Cable Communications Management, LLC
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package tools
import (
diff --git a/tools/spec-html.go b/tools/spec-html.go
index 4fee19b..01c47f4 100644
--- a/tools/spec-html.go
+++ b/tools/spec-html.go
@@ -1,51 +1,47 @@
-/* Copyright 2018 Comcast Cable Communications Management, LLC
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
+// Yo, check this out! We're setting up the building blocks for our cool tool.
package tools
import (
- "context"
- "encoding/json"
- "fmt"
- "io"
- "io/ioutil"
-
- "github.com/Comcast/sheens/core"
- "github.com/Comcast/sheens/interpreters/noop"
- . "github.com/Comcast/sheens/util/testutil"
- "github.com/jsccast/yaml"
-
- md "gopkg.in/russross/blackfriday.v2"
+ "context" // Gotta have this for managing goroutine lifetimes.
+ "encoding/json" // JSON is everywhere, so we'll need this to read and write it.
+ "fmt" // Basic input/output - can't live without it.
+ "io" // Working with input/output streams. Think of it like a data river!
+ "os" // New import for reading files in Go 1.21.
+
+ "github.com/Comcast/sheens/core" // This is where the magic happens in Sheens.
+ "github.com/Comcast/sheens/interpreters/noop" // A noop interpreter for when we want to do... nothing?
+ . "github.com/Comcast/sheens/util/testutil" // Test utils, but using the dot import for direct access.
+ "gopkg.in/yaml.v2" // YAML is JSON's cool cousin. We're using it for configuration.
+
+ md "github.com/russross/blackfriday/v2" // Markdown processor because text is boring without formatting.
)
+// RenderSpecHTML takes a Sheens Spec and turns it into HTML. Think of it as making a plain text look fancy on the web.
func RenderSpecHTML(s *core.Spec, out io.Writer) error {
f := func(format string, args ...interface{}) {
- fmt.Fprintf(out, format+"\n", args...)
+ fmt.Fprintf(out, format+"\n", args...) // Writing formatted output to our writer, adding a newline for readability.
}
+ // Using Blackfriday to turn Markdown into HTML. It's like magic for text!
f(`
%s
`, md.Run([]byte(s.Doc)))
- { // Nodes
+ { // Nodes section. Here we're gonna layout all the nodes in a neat table.
f(`
`)
- // Need to try to order these nodes sensibly.
+
+ // This inner function is a neat way to process each node. Encapsulation, baby!
fn := func(id string, node *core.Node) {
+ // For each node, we're creating a row in our HTML table.
f(`
%s
`, id, id)
+ // If a node has documentation, let's include that too. More info is always better!
if node.Doc != "" {
f(`
%s
`, md.Run([]byte(node.Doc)))
}
+ // If there's action source code, we'll show that in a
tag for formatting.
if node.ActionSource != nil {
f(`
%s
`, node.ActionSource.Source)
}
+ // Branches are tricky. They decide where to go next based on messages. It's like a choose-your-own-adventure book!
if node.Branches != nil {
if node.Branches.Type == "message" {
f(`
`)
for i, b := range node.Branches.Branches {
+ // Each branch gets its own row. It's like laying out options on a table.
f(`
%d
`, i)
f(`
`)
- // if b.Doc != "" {
- // f(`
pattern
`)
- // f(`
%s
`, JS(b.Pattern))
- // }
+
+ // Showing the pattern and guard of each branch. It's like setting rules for the adventure paths!
if b.Pattern != nil {
f(`
`, b.Target, b.Target)
@@ -79,10 +75,11 @@ func RenderSpecHTML(s *core.Spec, out io.Writer) error {
}
f(``)
}
+ // Making sure we always show the start node first. It's like starting at the beginning of the book.
if n, has := s.Nodes["start"]; has {
fn("start", n)
}
- // ToDo: Order.
+ // Looping through all nodes except "start" since we already handled it.
for id, node := range s.Nodes {
if id == "start" {
continue
@@ -95,17 +92,19 @@ func RenderSpecHTML(s *core.Spec, out io.Writer) error {
return nil
}
+// RenderSpecPage takes a spec and makes a whole web page out of it. This is where things get real.
func RenderSpecPage(s *core.Spec, out io.Writer, cssFiles []string, includeGraph bool) error {
if cssFiles == nil {
- cssFiles = []string{"/static/spec-html.css"}
+ cssFiles = []string{"/static/spec-html.css"} // Default styling if none provided. Gotta make it look good!
}
- js, err := json.Marshal(s)
+ js, err := json.Marshal(s) // Turning our spec into JSON because JavaScript can understand it.
if err != nil {
return err
}
+ // Basic HTML structure. This is like the skeleton of our web page.
fmt.Fprintf(out, `
@@ -113,6 +112,7 @@ func RenderSpecPage(s *core.Spec, out io.Writer, cssFiles []string, includeGraph
%s
`, s.Name)
+ // If we want to include a graph, we load up some extra JavaScript libraries for drawing.
if includeGraph {
fmt.Fprintf(out, `
@@ -124,6 +124,7 @@ func RenderSpecPage(s *core.Spec, out io.Writer, cssFiles []string, includeGraph
`, js)
}
+ // Linking to CSS files for styling. Everyone wants their page to be the prettiest, right?
for _, cssFile := range cssFiles {
fmt.Fprintf(out, " \n", cssFile)
}
@@ -134,10 +135,12 @@ func RenderSpecPage(s *core.Spec, out io.Writer, cssFiles []string, includeGraph
%s
`, s.Name)
+ // If we decided to include a graph, here's where it will show up. Like a map of our adventure!
if includeGraph {
fmt.Fprintf(out, ``)
}
+ // Here's where we render the spec into HTML. It's like filling the skeleton with muscles and skin.
if err = RenderSpecHTML(s, out); err != nil {
return err
}
@@ -150,26 +153,32 @@ func RenderSpecPage(s *core.Spec, out io.Writer, cssFiles []string, includeGraph
return nil
}
+// ReadAndRenderSpecPage reads a spec from a file, does some processing, and turns it into a web page.
func ReadAndRenderSpecPage(filename string, cssFiles []string, out io.Writer, includeGraph bool) error {
- specSrc, err := ioutil.ReadFile(filename)
+ // Reading the spec from a file. It's like opening a treasure chest!
+ specSrc, err := os.ReadFile(filename) // Updated to use os.ReadFile which is the recommended way since Go 1.16.
if err != nil {
return err
}
var spec core.Spec
+ // Unmarshalling YAML. It's like translating ancient scrolls into modern language.
if err = yaml.Unmarshal(specSrc, &spec); err != nil {
return err
}
+ // Setting up a noop interpreter. Sometimes, doing nothing is an important step.
interpreters := noop.NewInterpreters()
interpreters.I.Silent = true
+ // Contexts are great for managing go routines, like directing traffic in your code.
ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
+ defer cancel() // Always clean up after yourself. Cancel the context when you're done.
+ // Compiling the spec with our context and interpreter. It's like putting puzzle pieces together.
if err = spec.Compile(ctx, interpreters, true); err != nil {
return err
}
+ // Finally, we render our spec into a beautiful web page. Showtime!
return RenderSpecPage(&spec, out, cssFiles, includeGraph)
-
}
diff --git a/tools/spec-html.js b/tools/spec-html.js
index 6b20c5f..0220e6a 100644
--- a/tools/spec-html.js
+++ b/tools/spec-html.js
@@ -3,94 +3,106 @@
//
// Returns a function that takes state as an argument and updates the
// display.
-function renderMachine(mid, spec) {
- console.log("renderMachine", mid, spec)
+// initializeMachineRenderer doth conjure the visual semblance of a contraption, drawn from the quill of machineId and the tome of specification.
+// It trumpets the onset of this grand performance, erects a stage for the machine's display, and weaves the fabric of its graphical representation.
+// Hark! From its depths, it bestows upon us a function, updateDisplay, which, with great alacrity, alters the machine's visage based on the state's decree.
+function initializeMachineRenderer(machineId, specification) {
+ console.log("Initializing machine renderer", machineId, specification);
- var divid = "m_" + mid;
+ // machineDivId, by the machineId's whisper, doth craft a moniker unique for the machine's div sanctuary.
+ const machineDivId = `m_${machineId}`;
- var div = d3.select("#graph")
- .append("div")
- .attr("id", divid);
- if (mid) {
- div.append("div")
- .classed("machineId", true)
- .text(mid);
- }
- var gdivid = divid + "_graph";
- var gfiv = div.append("div")
- .classed("graph", true)
- .attr("id", gdivid)
+ // machineDiv, with a gesture grand, summons the parent graph to embrace a new div, anointing it with the sacred ID.
+ const machineDiv = d3.select("#graph")
+ .append("div")
+ .attr("id", machineDivId);
- var elements = [];
- if (spec && spec.nodes) {
- for (var nodeName in spec.nodes) {
- elements.push({data: {id: nodeName, link: "#" + nodeName}});
- var node = spec.nodes[nodeName];
- if (node.branching && node.branching.branches) {
- var branches = node.branching.branches;
- for (var i = 0; i < branches.length; i++) {
- var branch = branches[i];
- if (branch.target) {
- elements.push({data:
- {id: nodeName + "_" + i,
- source: nodeName,
- target: branch.target}});
- }
- }
- }
- }
+ // Should the machineId grace us with its presence, a div shall rise to herald its name, adorned with "machineId" for distinction.
+ if (machineId) {
+ machineDiv.append("div")
+ .classed("machineId", true)
+ .text(machineId);
}
- console.log("elements", elements);
-
- var cy = cytoscape({
- container: document.getElementById(gdivid),
- elements: elements,
- style: [
- {
- selector: 'node',
- style: {
- 'content': 'data(label)',
- 'background-color': '#666',
- 'label': 'data(id)'
- }
- },
-
- {
- selector: 'edge',
- style: {
- 'curve-style': 'bezier',
- 'target-arrow-shape': 'triangle',
- 'width': 1,
- 'line-color': 'blue',
- 'target-arrow-color': 'orange',
- 'target-arrow-shape': 'triangle',
- 'label': 'data(label)',
- }
- }
- ],
+ // graphDivId, with foresight clear, foretells the ID of the graph's own chamber within the machine's domain.
+ const graphDivId = `${machineDivId}_graph`;
+ // graphDiv, with delicate craft, nestles a div within the machine's embrace, bestowing upon it the name graphDivId, and "graph" as its title.
+ machineDiv.append("div")
+ .classed("graph", true)
+ .attr("id", graphDivId);
- layout: {
- name: 'breadthfirst',
- directed: true,
- rows: 1
- }
+ // graphElements, a troupe of shadows, awaits in silence to play their parts as nodes and edges in this visual feast.
+ const graphElements = [];
+
+ // This solemn act reads from the specification's script, casting nodes and edges to take their places upon our stage.
+ if (specification && specification.nodes) {
+ for (const nodeName in specification.nodes) {
+ graphElements.push({data: {id: nodeName, link: `#${nodeName}`}});
+
+ const node = specification.nodes[nodeName];
+ if (node.branching && node.branching.branches) {
+ node.branching.branches.forEach((branch, index) => {
+ if (branch.target) {
+ graphElements.push({
+ data: {
+ id: `${nodeName}_${index}`,
+ source: nodeName,
+ target: branch.target
+ }
+ });
+ }
+ });
+ }
+ }
+ }
+ console.log("Graph elements", graphElements);
+
+ // cy, by magick's hand, brings forth Cytoscape, its vessel filled with the essence of graphDivId, the elements of our tale, and the laws of style and form.
+ const cy = cytoscape({
+ container: document.getElementById(graphDivId),
+ elements: graphElements,
+ style: [
+ {
+ selector: 'node',
+ style: {
+ 'content': 'data(label)',
+ 'background-color': '#666',
+ 'label': 'data(id)'
+ }
+ },
+ {
+ selector: 'edge',
+ style: {
+ 'curve-style': 'bezier',
+ 'target-arrow-shape': 'triangle',
+ 'width': 1,
+ 'line-color': 'blue',
+ 'target-arrow-color': 'orange',
+ 'label': 'data(label)',
+ }
+ }
+ ],
+ layout: {
+ name: 'breadthfirst',
+ directed: true,
+ rows: 1
+ }
});
- cy.edges().on("tap", function(){ alert(this); });
+ // Upon the edge's touch, a summoning cry: an alert that springs forth with the edge's tale.
+ cy.edges().on("tap", function() { alert(this); });
- return function(state) {
- stateDiv.text(JSON.stringify(state.bs));
- console.log("state", state);
- cy.elements("node").style({"background-color":"gray"});
- cy.$('#' + state.node).style({"background-color": "red"});
+ // updateDisplay, a seer's vision, that with nimble touch, alters the hues of nodes to mirror the state's current guise.
+ return function updateDisplay(state) {
+ console.log("State update", state);
+ cy.elements("node").style({"background-color":"gray"});
+ cy.$(`#${state.node}`).style({"background-color": "red"});
};
}
+// As the curtain rises with the window's load, so too is the machine renderer summoned, empty of machineId, yet full of the script thisSpec.
window.addEventListener("load", function(evt) {
- renderMachine("", thisSpec);
-});
-
-
+ initializeMachineRenderer("", thisSpec);
+});
\ No newline at end of file
diff --git a/util/testutil/testutil.go b/util/testutil/testutil.go
index 60ce7b2..b7fe81c 100644
--- a/util/testutil/testutil.go
+++ b/util/testutil/testutil.go
@@ -10,6 +10,7 @@
* limitations under the License.
*/
+// Package testutil provides some utility functions for Go tests.
package testutil
import (
@@ -28,7 +29,7 @@ func JS(x interface{}) string {
return string(bs)
}
-// Dwimjs, when given a string or bytes, parses that data as JSON.
+// Dwimjs when given a string or bytes, parses that data as JSON.
// When given anything else, just returns what's given.
//
// See https://en.wikipedia.org/wiki/DWIM.