jtree icon indicating copy to clipboard operation
jtree copied to clipboard

Alternative syntax for Stamp language

Open Sarcasm opened this issue 5 years ago • 14 comments

Right now the stamp language provides this syntax:

folder myProject
file myProject/index.js
 data
  console.log("Hello world")
file myProject/readme.md
 data
  # My Readme

I'm wondering if it could not be simplified like this:

myProject/
 index.js
  console.log("Hello world")
 readme.md
  # My Readme

Directories can discriminated from files thanks to the final /. Children of directories are files. Children of files, is file content.

Would that still qualify as a treenotation language?

Sarcasm avatar Feb 25 '21 22:02 Sarcasm

Love it! How would you do file permissions?

Also, you could come up with your own name. Building a tree language is cheap so could build a whole new one.

Happy to team up on a Twitch call and pair program one out.

breck7 avatar Feb 25 '21 23:02 breck7

I guess you could add the restriction to not allow spaces in filenames, then just do index.js Writeable or something. Lots of options

breck7 avatar Feb 25 '21 23:02 breck7

For permission yes, I think it could be specified after the filename. Maybe a bad idea, but could be in a chmod-like format:

foo.py +x
 #!/usr/bin/env python
 print("hello world")

Sarcasm avatar Feb 25 '21 23:02 Sarcasm

Here's a quick prototype. I just started it but gotta run to a conference

https://jtree.treenotation.org/designer/#grammar%0A%20tooling%20onsave%20jtree%20build%20produceLang%20stamp%0A%20todo%20File%20permissions%0A%20anyCell%0A%20extraCell%0A%20%20highlightScope%20invalid%0A%20anyCell%0A%20%20highlightScope%20string%0A%20promptWordsCell%0A%20%20highlightScope%20string%0A%20filepathCell%0A%20%20highlightScope%20keyword%0A%20varNameCell%0A%20%20highlightScope%20string%0A%20commentCell%0A%20%20highlightScope%20comment%0A%20inputTypeCell%0A%20%20enum%20string%20int%20any%20lowercase%0A%20keywordCell%0A%20%20highlightScope%20keyword.control%0A%20stampNode%0A%20%20root%0A%20%20description%20A%20prefix%20Tree%20Language%20for%20creating%20distributable%20text%20template%20files%20that%20expand%20to%20folders%20and%20files.%0A%20%20catchAllNodeType%20errorNode%0A%20%20javascript%0A%20%20%20async%20executeSeries(parentDir)%20%7B%0A%20%20%20%20const%20length%20%3D%20this.length%0A%20%20%20%20for%20(let%20index%20%3D%200%3B%20index%20%3C%20length%3B%20index%2B%2B)%20%7B%0A%20%20%20%20%20const%20node%20%3D%20this.nodeAt(index)%0A%20%20%20%20%20await%20node.execute(parentDir)%0A%20%20%20%20%7D%0A%20%20%20%20return%20parentDir%0A%20%20%20%7D%0A%20%20%20async%20execute(parentDir%20%3D%20process.cwd())%20%7B%0A%20%20%20%20await%20this.executeSeries(parentDir)%0A%20%20%20%7D%0A%20%20%20static%20dirToStampWithContents(absPathWithoutEndingSlash)%20%7B%0A%20%20%20%20%20return%20stampNode._dirToStampFn(absPathWithoutEndingSlash%2C%20%22content%22)%0A%20%20%20%7D%0A%20%20%20static%20dirToStamp(absPathWithoutEndingSlash)%20%7B%0A%20%20%20%20%20return%20stampNode._dirToStampFn(absPathWithoutEndingSlash%2C%20%22list%22)%0A%20%20%20%7D%0A%20%20%20static%20_dirToStampFn(absPathWithoutEndingSlash%2C%20output)%20%7B%0A%20%20%20%20const%20fs%20%3D%20require(%22fs%22)%0A%20%20%20%20%2F%2F%20todo%3A%20add%20chmod%2C%20file%20metadata%0A%20%20%20%20if%20(absPathWithoutEndingSlash.startsWith(%22.%22))%20absPathWithoutEndingSlash%20%3D%20jtree.Utils.resolvePath(absPathWithoutEndingSlash%2C%20process.cwd()%20%2B%20%22%2F%22)%0A%20%20%20%20const%20stat%20%3D%20fs.statSync(absPathWithoutEndingSlash)%0A%20%20%20%20if%20(!stat.isDirectory())%20throw%20new%20Error(%60%24%7BabsPath%7D%20is%20a%20file%20not%20a%20directory.%60)%0A%20%20%20%20const%20fns%20%3D%20%7B%0A%20%20%20%20%20list%3A%20(file%2C%20reducedPath)%20%3D%3E%20%7B%0A%20%20%20%20%20%20const%20stat%20%3D%20fs.statSync(file)%0A%20%20%20%20%20%20const%20isDir%20%3D%20stat.isDirectory()%0A%20%20%20%20%20%20if%20(isDir)%20return%20%60folder%20%60%20%2B%20reducedPath%0A%20%20%20%20%20%20return%20%60file%20%60%20%2B%20reducedPath%0A%20%20%20%20%20%7D%2C%0A%20%20%20%20%20content%3A%20(file%2C%20reducedPath)%20%3D%3E%20%7B%0A%20%20%20%20%20%20const%20stat%20%3D%20fs.statSync(file)%0A%20%20%20%20%20%20const%20isDir%20%3D%20stat.isDirectory()%0A%20%20%20%20%20%20if%20(isDir)%20return%20%60folder%20%60%20%2B%20reducedPath%0A%20%20%20%20%20%20const%20content%20%3D%20fs.readFileSync(file%2C%20%22utf8%22)%0A%20%20%20%20%20%20return%20%60file%20%24%7BreducedPath%7D%0A%20%20%20%20data%24%7Bjtree.TreeNode.nest(content%2C%202)%7D%60%0A%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%20%20const%20fn%20%3D%20fns%5Boutput%5D%0A%20%20%20%20return%20this._dirToStamp(absPathWithoutEndingSlash%2C%20fn)%0A%20%20%20%7D%0A%20%20%20static%20_dirToStamp(absPathWithoutEndingSlash%2C%20fileFn)%20%7B%0A%20%20%20%20const%20files%20%3D%20require(%22recursive-readdir-sync%22)(absPathWithoutEndingSlash)%0A%20%20%20%20const%20folderParts%20%3D%20absPathWithoutEndingSlash.split(%22%2F%22)%0A%20%20%20%20const%20rootFolderName%20%3D%20folderParts.pop()%0A%20%20%20%20const%20rootFolderPath%20%3D%20folderParts.join(%22%2F%22)%0A%20%20%20%20const%20pathStartIndex%20%3D%20rootFolderPath.length%20%2B%201%0A%20%20%20%20return%20files.map(file%20%3D%3E%20fileFn(file%2C%20file.substr(pathStartIndex))).join(%22%5Cn%22)%0A%20%20%20%7D%0A%20%20inScope%20hashbangNode%20folderNode%20fileNode%0A%20hashbangNode%0A%20%20crux%20%23!%0A%20%20catchAllCellType%20commentCell%0A%20%20cells%20commentCell%0A%20catchAllAnyLineNode%0A%20%20catchAllCellType%20anyCell%0A%20%20catchAllNodeType%20catchAllAnyLineNode%0A%20%20cells%20anyCell%0A%20errorNode%0A%20%20baseNodeType%20errorNode%0A%20fileNode%0A%20%20cells%20filepathCell%0A%20%20javascript%0A%20%20%20compileToBash(parentDir)%20%7B%0A%20%20%20%20const%20filePath%20%3D%20this._getAbsolutePath(parentDir)%0A%20%20%20%20return%20%60touch%20%24%7BfilePath%7D%5Cnecho%20-e%20%22%24%7Bthis.childrenToString()%7D%22%20%3E%3E%20%24%7BfilePath%7D%60%0A%20%20%20%7D%0A%20%20%20_getAbsolutePath(parentDir%20%3D%20process.cwd())%20%7B%0A%20%20%20%20return%20parentDir%20%2B%20%22%2F%22%20%2B%20this.cells.filepathCell%0A%20%20%20%7D%0A%20%20%20execute(parentDir)%20%7B%0A%20%20%20%20const%20fs%20%3D%20require(%22fs%22)%0A%20%20%20%20const%20fullPath%20%3D%20this._getAbsolutePath(parentDir)%0A%20%20%20%20console.log(%60Creating%20file%20%24%7BfullPath%7D%60)%0A%20%20%20%20const%20data%20%3D%20this.getNode(%22data%22)%0A%20%20%20%20const%20content%20%3D%20data%20%3F%20data.childrenToString()%20%3A%20%22%22%0A%20%20%20%20require(%22mkdirp%22).sync(require(%22path%22).dirname(fullPath))%0A%20%20%20%20fs.writeFileSync(fullPath%2C%20content%2C%20%22utf8%22)%0A%20%20%20%20const%20isExecutable%20%3D%20this.has(%22executable%22)%20%2F%2F%20todo%3A%20allow%20for%20all%20file%20permissions%3F%0A%20%20%20%20if%20(isExecutable)%20fs.chmodSync(fullPath%2C%20%22755%22)%0A%20%20%20%7D%0A%20%20inScope%20dataNode%0A%20%20catchAllNodeType%20catchAllAnyLineNode%0A%20folderNode%0A%20%20cells%20filepathCell%0A%20%20catchAllNodeType%20fileNode%0A%20%20pattern%20%5C%2F%0A%20%20javascript%0A%20%20%20compileToBash(parentDir)%20%7B%0A%20%20%20%20return%20%60mkdir%20%24%7Bthis._getAbsolutePath(parentDir)%7D%60%0A%20%20%20%7D%0A%20%20%20_getAbsolutePath(parentDir%20%3D%20process.cwd())%20%7B%0A%20%20%20%20return%20parentDir%20%2B%20%22%2F%22%20%2B%20this.cells.filepathCell%0A%20%20%20%7D%0A%20%20%20execute(parentDir)%20%7B%0A%20%20%20%20const%20path%20%3D%20this._getAbsolutePath(parentDir)%0A%20%20%20%20console.log(%60Creating%20folder%20%24%7Bpath%7D%60)%0A%20%20%20%20require(%22mkdirp%22).sync(path)%0A%20%20%20%7D%0Asample%0A%20myProject%2F%0A%20%20index.js%0A%20%20%20console.log(%22Hello%20world%22)%0A%20%20readme.md%0A%20%20%20%23%20My%20Readme

breck7 avatar Feb 26 '21 00:02 breck7

Maybe the presence of a shebang is enough to warrant the execution bit. And like now, other permissions could be omitted.

Sarcasm avatar Feb 26 '21 00:02 Sarcasm

You know what I only use that execution bit once in a blue moon. This Stamp alternative could be dead simple (expands to plain vanilla non-executable files) without file permissions.

Just playing with the demo in the Designer I like yours better than mine.

It reminds me of a better https://www.gnu.org/software/emacs/manual/html_node/emacs/Dired.html

breck7 avatar Feb 26 '21 15:02 breck7

Would you be interested in making your own repo and running with that idea?

I could contribute over there. I'd love to hear how much the experience currently sucks of building one's own Tree Language (my guess is very bad).

I think it would be worthwhile and would be a user myself.

breck7 avatar Feb 26 '21 15:02 breck7

I attempted something (link at the end). Not sure I will push it further but I was happy to give it a try.

Regarding my experience.

Some nice things:

  • The IDE:
    • Completion in the text editor, although this tree language does not benefit from it, the grammar did.
    • "Explain" was very useful when writing the grammar.
  • The various examples, in the IDE and on Github were useful to understand how to make a grammar.
  • When thinking about Domain-specific language (DSL), I like to give a tree notation representation a try, I like its terseness.

Less nice things:

  • The IDE:
    • Would be nice to have a bigger text editor in the IDE.
    • I understand "Execute" and "Explain", but not "Compile". A Tool Tip for these actions could be helpful.
    • The auto-generated Readme has a lot of wasted space, multiple lines between a section title and its content.
    • Typing multiple f in the text editor produce something weird, the fs get smaller / closer to each others (font ligature?).
    • The errors, grammar and language could be better integrated. Right now, grammar errors are in the same pane as the auto-generated Readme, it's a bit confusing. In my browser (Firefox), the inline errors do not appear immediately, unlike the error lists ("Grammar/Language Errors"). I have to type a whitespace to see the inline error. But if I move the cursor with the mouse I do not see it.
  • Some examples weren't up-to-date / complete. For example the Chuck example does not execute, I did not realized initially that it was a todo. Maybe "Execute" could be greyed out in this case or an message indicate that execute() isn't implemented.
  • Finishing the grammar with a newline is an error. Maybe newlines should not be the separator, but instead something like an imaginary 'startOfLine' character. Right now newlines (nodeBreakSymbol) 'separate' nodes, but maybe there should be the possibility to set other modes so that it 'start' a new node.

https://jtree.treenotation.org/designer/#grammar%0A%20anyCell%0A%20%20highlightScope%20string%0A%20filePathCell%0A%20%20highlightScope%20keyword%0A%20stampNode%0A%20%20root%0A%20%20inScope%20directoryNode%0A%20%20catchAllNodeType%20fileNode%0A%20%20javascript%0A%20%20%20execute()%20%7B%0A%20%20%20%20return%20this.map(child%20%3D%3E%20child.execute()).join(%22%5Cn%22)%0A%20%20%20%7D%0A%20%20example%0A%20%20%20empty.txt%0A%20%20%20foo.txt%0A%20%20%20%20foo%0A%20%20%20%20bar%0A%20%20%20%20baz%0A%20%20%20executable.sh%0A%20%20%20%20%23!%2Fbin%2Fsh%0A%20%20%20%20echo%20%22Do%20you%20have%205%24%3F%22%0A%20%20%20foo%2F%0A%20%20%20%20__init__.py%0A%20%20%20%20%20__version__%20%3D%20'0.0.1'%0A%20%20%20%20bar%2F%0A%20%20%20%20%20__init__.py%0A%20directoryNode%0A%20%20cells%20filePathCell%0A%20%20catchAllCellType%20filePathCell%0A%20%20inScope%20directoryNode%0A%20%20catchAllNodeType%20fileNode%0A%20%20pattern%20%5C%2F%24%0A%20%20javascript%0A%20%20%20execute()%20%7B%0A%20%20%20%20%20const%20name%20%3D%20this.getLine()%0A%20%20%20%20%20if%20(name.indexOf('%2F')%20!%3D%20name.length%20-%201)%20%7B%0A%20%20%20%20%20%20%20throw%20%60Invalid%20directory%20name%3A%20%24%7Bname%7D%60%0A%20%20%20%20%20%7D%0A%20%20%20%20%20return%20%5B%0A%20%20%20%20%20%20%20%60mkdir%20-p%20%24%7Bname%7D%60%2C%0A%20%20%20%20%20%20%20%60pushd%20%24%7Bname%7D%60%2C%0A%20%20%20%20%20%20%20%5D.concat(%0A%20%20%20%20%20%20%20this.map(child%20%3D%3E%20child.execute())%0A%20%20%20%20%20).concat(%5B%0A%20%20%20%20%20%20%20%22popd%22%0A%20%20%20%20%20%5D).join(%22%5Cn%22)%0A%20%20%20%7D%0A%20fileNode%0A%20%20cells%20filePathCell%0A%20%20catchAllCellType%20filePathCell%0A%20%20catchAllNodeType%20catchAllAnyLineNode%0A%20%20javascript%0A%20%20%20execute()%20%7B%0A%20%20%20%20%20const%20filename%20%3D%20this.getLine()%0A%20%20%20%20%20if%20(filename.indexOf('%2F')%20!%3D%20-1)%20%7B%0A%20%20%20%20%20%20%20throw%20%60Invalid%20filename%3A%20%24%7Bfilename%7D%60%0A%20%20%20%20%20%7D%0A%20%20%20%20%20const%20lines%20%3D%20this.getLines()%0A%20%20%20%20%20if%20(lines.indexOf(%22EOF%22)%20!%3D%20-1)%20%7B%0A%20%20%20%20%20%20%20throw%20%60%24%7Bfilename%7D%3A%20Unsupported%20line%3A%20EOF%60%3B%0A%20%20%20%20%20%7D%20%0A%20%20%20%20%20if%20(lines.length%20%3D%3D%200)%20%7B%0A%20%20%20%20%20%20%20return%20%60touch%20%24%7Bfilename%7D%60%0A%20%20%20%20%20%7D%0A%20%20%20%20%20let%20out%20%3D%20%5B%0A%20%20%20%20%20%20%20%60%3E%24%7Bfilename%7D%20%3C%3C'EOF'%0A%20%20%20%24%7Blines.join(%22%5Cn%22)%7D%0A%20%20%20EOF%60%0A%20%20%20%20%20%5D%0A%20%20%20%20%20if%20(lines%5B0%5D.startsWith(%22%23!%22))%20%7B%0A%20%20%20%20%20%20%20out.push(%60chmod%20%2Bx%20%24%7Bfilename%7D%60)%0A%20%20%20%20%20%7D%0A%20%20%20%20%20return%20out.join(%22%5Cn%22)%0A%20%20%20%7D%0A%20catchAllAnyLineNode%0A%20%20cells%20anyCell%0A%20%20catchAllCellType%20anyCell%0A%20%20catchAllNodeType%20catchAllAnyLineNode%0A%20%20javascript%0A%20%20%20getTextContent()%20%7B%0A%20%20%20%20%20return%20this.getLine()%0A%20%20%20%7D%0Asample%0A%20empty.txt%0A%20foo.txt%0A%20%20foo%0A%20%20bar%0A%20%20baz%0A%20executable.sh%0A%20%20%23!%2Fbin%2Fsh%0A%20%20echo%20%22Do%20you%20have%205%24%3F%22%0A%20foo%2F%0A%20%20__init__.py%0A%20%20%20__version__%20%3D%20'0.0.1'%0A%20%20bar%2F%0A%20%20%20__init__.py

Sarcasm avatar Feb 26 '21 23:02 Sarcasm

@Sarcasm finally got back to this. This is awesome!

I find it way better than Stamp. Feels natural. I made a few tiny changes to your code above (just changed execute methods to compile for consistency. The other reductions in code were just to temporarily make the link shorter). Named it "Clay". Playing around with it made me think of shaping a full project fluidly.

I want to go through your feedback and address the individual points, but just wanted to say this is awesome. Better than Stamp and really as simple as possible. Kudos.

https://jtree.treenotation.org/designer/#grammar%0A%20anyCell%0A%20%20highlightScope%20string%0A%20filePathCell%0A%20%20highlightScope%20keyword%0A%20clayNode%0A%20%20description%20Edit%20an%20entire%20project%20as%20a%20single%20text%20file.%0A%20%20root%0A%20%20inScope%20directoryNode%0A%20%20catchAllNodeType%20fileNode%0A%20%20javascript%0A%20%20%20compile()%20%7B%20return%20this.map(child%20%3D%3E%20child.compile()).join(%22%5Cn%22)%7D%0A%20%20example%0A%20%20%20foo.txt%0A%20%20%20%20foo%0A%20directoryNode%0A%20%20cells%20filePathCell%0A%20%20catchAllCellType%20filePathCell%0A%20%20inScope%20directoryNode%0A%20%20catchAllNodeType%20fileNode%0A%20%20pattern%20%5C%2F%24%0A%20%20javascript%0A%20%20%20compile()%20%7B%0A%20%20%20%20%20const%20name%20%3D%20this.getLine()%0A%20%20%20%20%20return%20%5B%0A%20%20%20%20%20%20%20%60mkdir%20-p%20%24%7Bname%7D%60%2C%0A%20%20%20%20%20%20%20%60pushd%20%24%7Bname%7D%60%2C%0A%20%20%20%20%20%20%20%5D.concat(%0A%20%20%20%20%20%20%20this.map(child%20%3D%3E%20child.compile())%0A%20%20%20%20%20).concat(%5B%22popd%22%5D).join(%22%5Cn%22)%0A%20%20%20%7D%0A%20fileNode%0A%20%20cells%20filePathCell%0A%20%20catchAllCellType%20filePathCell%0A%20%20catchAllNodeType%20catchAllAnyLineNode%0A%20%20javascript%0A%20%20%20compile()%20%7B%0A%20%20%20%20%20const%20filename%20%3D%20this.getLine()%0A%20%20%20%20%20if%20(filename.indexOf('%2F')%20!%3D%20-1)%0A%20%20%20%20%20%20%20throw%20%60Invalid%20filename%3A%20%24%7Bfilename%7D%60%0A%20%20%20%20%20const%20lines%20%3D%20this.getLines()%0A%20%20%20%20%20if%20(lines.length%20%3D%3D%200)%0A%20%20%20%20%20%20%20return%20%60touch%20%24%7Bfilename%7D%60%0A%20%20%20%20%20let%20out%20%3D%20%5B%0A%20%20%20%20%20%20%20%60%3E%24%7Bfilename%7D%20%3C%3C'EOF'%0A%20%20%20%24%7Blines.join(%22%5Cn%22)%7D%0A%20%20%20EOF%60%0A%20%20%20%20%20%5D%0A%20%20%20%20%20if%20(lines%5B0%5D.startsWith(%22%23!%22))%0A%20%20%20%20%20%20%20out.push(%60chmod%20%2Bx%20%24%7Bfilename%7D%60)%0A%20%20%20%20%20return%20out.join(%22%5Cn%22)%0A%20%20%20%7D%0A%20catchAllAnyLineNode%0A%20%20cells%20anyCell%0A%20%20catchAllCellType%20anyCell%0A%20%20catchAllNodeType%20catchAllAnyLineNode%0A%20%20javascript%0A%20%20%20getTextContent()%20%7B%20return%20this.getLine()%20%7D%0Asample%0A%20readme.md%0A%20%20%23%20Click%20%22Compile%22%0A%20%20Get%20a%20bash%20script%20that%20creates%20your%20project.%0A%20%20Read%20more%3A%20https%3A%2F%2Fgithub.com%2Fpublicdomaincompany%2Fjtree%2Fissues%2F120%0A%20package.json%0A%20%20%7B%0A%20%20%20%22name%22%3A%22clay%22%0A%20%20%7D%0A%20executable.sh%0A%20%20%23!%2Fbin%2Fsh%0A%20%20echo%20%22Hello%20world%22%0A%20src%2F%0A%20%20app.js%0A%20%20%20console.log(%22Hello%20world%22)

breck7 avatar Aug 06 '21 07:08 breck7

Hello,

I just try the Clay example, and it looks like there is an issue with the package.json generated content:

image

Sarcasm avatar Aug 11 '21 09:08 Sarcasm

You are right. This should work:

https://jtree.treenotation.org/designer/#grammar%0A%20anyCell%0A%20%20highlightScope%20string%0A%20filePathCell%0A%20%20highlightScope%20keyword%0A%20clayNode%0A%20%20description%20Edit%20an%20entire%20project%20as%20a%20single%20text%20file.%0A%20%20root%0A%20%20inScope%20directoryNode%0A%20%20catchAllNodeType%20fileNode%0A%20%20javascript%0A%20%20%20compile()%20%7B%20return%20this.map(child%20%3D%3E%20child.compile()).join(%22%5Cn%22)%7D%0A%20%20example%0A%20%20%20foo.txt%0A%20%20%20%20foo%0A%20directoryNode%0A%20%20cells%20filePathCell%0A%20%20catchAllCellType%20filePathCell%0A%20%20inScope%20directoryNode%0A%20%20catchAllNodeType%20fileNode%0A%20%20pattern%20%5C%2F%24%0A%20%20javascript%0A%20%20%20compile()%20%7B%0A%20%20%20%20%20const%20name%20%3D%20this.getLine()%0A%20%20%20%20%20return%20%5B%0A%20%20%20%20%20%20%20%60mkdir%20-p%20%24%7Bname%7D%60%2C%0A%20%20%20%20%20%20%20%60pushd%20%24%7Bname%7D%60%2C%0A%20%20%20%20%20%20%20%5D.concat(%0A%20%20%20%20%20%20%20this.map(child%20%3D%3E%20child.compile())%0A%20%20%20%20%20).concat(%5B%22popd%22%5D).join(%22%5Cn%22)%0A%20%20%20%7D%0A%20fileNode%0A%20%20cells%20filePathCell%0A%20%20catchAllCellType%20filePathCell%0A%20%20catchAllNodeType%20catchAllAnyLineNode%0A%20%20javascript%0A%20%20%20compile()%20%7B%0A%20%20%20%20%20const%20filename%20%3D%20this.getLine()%0A%20%20%20%20%20if%20(filename.indexOf('%2F')%20!%3D%20-1)%0A%20%20%20%20%20%20%20throw%20%60Invalid%20filename%3A%20%24%7Bfilename%7D%60%0A%20%20%20%20%20const%20contents%20%3D%20this.childrenToString()%0A%20%20%20%20%20if%20(!contents.length)%0A%20%20%20%20%20%20%20return%20%60touch%20%24%7Bfilename%7D%60%0A%20%20%20%20%20let%20out%20%3D%20%5B%0A%20%20%20%20%20%20%20%60%3E%24%7Bfilename%7D%20%3C%3C'EOF'%0A%20%20%20%24%7Bcontents%7D%0A%20%20%20EOF%60%0A%20%20%20%20%20%5D%0A%20%20%20%20%20if%20(contents.startsWith(%22%23!%22))%0A%20%20%20%20%20%20%20out.push(%60chmod%20%2Bx%20%24%7Bfilename%7D%60)%0A%20%20%20%20%20return%20out.join(%22%5Cn%22)%0A%20%20%20%7D%0A%20catchAllAnyLineNode%0A%20%20cells%20anyCell%0A%20%20catchAllCellType%20anyCell%0A%20%20catchAllNodeType%20catchAllAnyLineNode%0Asample%0A%20readme.md%0A%20%20%23%20Click%20%22Compile%22%0A%20%20Get%20a%20bash%20script%20that%20creates%20your%20project.%0A%20%20Read%20more%3A%20https%3A%2F%2Fgithub.com%2Fpublicdomaincompany%2Fjtree%2Fissues%2F120%0A%20package.json%0A%20%20%7B%0A%20%20%20%22name%22%3A%22clay%22%0A%20%20%7D%0A%20executable.sh%0A%20%20%23!%2Fbin%2Fsh%0A%20%20echo%20%22Hello%20world%22%0A%20src%2F%0A%20%20app.js%0A%20%20%20console.log(%22Hello%20world%22)

breck7 avatar Aug 11 '21 16:08 breck7

Two reasons for this:

  1. The core TreeNode methods are not great. Some methods I often make mistakes with, such as getLines. I think some subpar methods could removed, some can be better named, and some more helpful core methods could be added. Perhaps there should be fewer public methods, but the ones remaining should be fantastic and foolproof. Or maybe there's a missing pattern(s) that would make core easier to use. Not sure. All I know is that I wish the core API was better!

  2. In this specific example, I changed the compilation of the parent file block to use childrenToString instead of getLines. Given a Tree like:

a
 b
  c
  d

Then "childrenToString" of "a" returns simply the text block:

b
 c
 d

I find this to be a very common pattern when embedding non-tree languages in a tree language and use it often. Using methods designed for tree languages inside "blob", non-tree language blocks can be tricky, and generally just dumping those blobs is what you want, in which case "childrenToString" works great. This situation should probably be well documented in future Grammar docs. The code currently parses non-Tree language blobs like this just like any other Tree Node, and I think there are some theoretical issues that could be resolved there. It's definitely one of the places that needs some good FAQs, as I was just having a long conversation with another dev on this very topic.

breck7 avatar Aug 11 '21 16:08 breck7

Just for clarity, a diff of the fix: Screen Shot 2021-08-11 at 6 36 18 AM

breck7 avatar Aug 11 '21 16:08 breck7

The fix makes sense, childrenToString() looks more natural/correct than iterating over the lines, thank you.

Sarcasm avatar Aug 11 '21 16:08 Sarcasm

I finally got around to this!

https://scroll.pub/blog/stamp.html

breck7 avatar Jun 23 '24 17:06 breck7