[HN Gopher] KeenType: Pure Java typesetting system ___________________________________________________________________ KeenType: Pure Java typesetting system Author : todsacerdoti Score : 67 points Date : 2023-01-23 17:01 UTC (5 hours ago) (HTM) web link (github.com) (TXT) w3m dump (github.com) | whartung wrote: | What a fascinating license. | | This is a particularly compelling line from it: | 3. You must not distribute the modified file with the filename of | the original file. | | Just in isolation, outside of the other aspects of the license, | this is pretty much completely crippling for a Java program. | | Simply, if you change a file, you can't use the same name. Well, | in Java, the name of the file is the name of the class. You can't | have the file name differ from the class inside. | | That means you can't change a Java file in this codebase. | | Fascinating. | [deleted] | [deleted] | KRAKRISMOTT wrote: | Or just use a shading tool (which comes by default with most | Maven/Gradle builds). It is not a barrier for any experienced | java developer. | hbn wrote: | Now I want to make a fork, DumbKeenType, where the first thing | I do is append "Dumb" to the beginning of every class/filename. | | https://www.newyorker.com/tech/annals-of-technology/dumb-sta... | dcminter wrote: | Huh? Refactor the class name in virtually any Java supporting | IDE and the file name and invocations will be changed along | with it. | | The license term seems petty, and would be annoying, but the | workaround is trivial. | whartung wrote: | If you do that, you have to rename every class file it | impacts as well, because now those files are changed. A lot | of refactoring systems don't take into account the ancillary | files (like build files, start scripts, etc.). | | So, the workaround can be quite impactful in the end. | Renaming the entirety of the code base, potentially. | dcminter wrote: | Oh, duh, now I get it. That's very annoying. | layer8 wrote: | In principle, you could rename the classes on the class- | file level after compilation, back to the original names. | That wouldn't violate the license. | | Moreover, under the following definition in the license: | `Modification' of a file means any procedure that | produces a derivative file under any applicable law | -- that is, a file containing the original file or a | significant portion of it, either verbatim or with | modifications and/or translated into another | language. | | ...compilation would count as modification ("translated | into another language"), and compilation also changes the | file name (from .java to .class), so distributing the | compilation output should be fine. Furthermore, any changes | to the sources can be distributed as a diff patch, which | also has a different file name. | thangalin wrote: | > but the workaround is trivial. | | If you have a trivial solution to rename hundreds of Java | classes and their references en masse without breaking the | build and without renaming each file one-at-a-time, do tell. | You can find more details about potential solutions I've | tried here: | | https://stackoverflow.com/questions/75154524/bulk-rename- | of-... | | FYI, the following plug-in for IDEA supports regular | expressions (as of Jan 20, 2023), so it may be a viable | solution: | | https://plugins.jetbrains.com/plugin/17455-rename-files- | refa... | counttheforks wrote: | Posted a solution on your other comment: | https://news.ycombinator.com/item?id=34493760 | playingalong wrote: | I bet Open Rewrite can handle that quite easily: | https://docs.openrewrite.org/ | thangalin wrote: | I wasn't able to find a recipe that performs a regular | expression rename. That is, there doesn't seem to be | anything like: --- type: | specs.openrewrite.org/v1beta/recipe name: | com.yourorg.ChangeTypeExample displayName: Change | type example recipeList: - | org.openrewrite.java.ChangeType: | oldFullyQualifiedTypeName: (.+) | newFullyQualifiedTypeName: Kt$1 | [deleted] | Someone wrote: | I would do that the hacker way: | | - build the current TeX.jar | | - jar tf TeX.jar | grep _whatever_ to get a list of to- | be-renamed classes | | - use _sed_ or manual editing to generate a large list of | recipes | | - run those recipes | | Instead of _jar tf_ , grep for "^package foo.bar.baz" and | "class (complex regex)" in the source code to get a list | of package and class names, or use the directory | structure. Maybe do all of them, and compare the results | for robustness. | | That list will be large and it may not go perfect in one | go, but it's a one-off job, and you'll get there. | dangerlibrary wrote: | This feels like something where the right solution is to | drop down to the terminal and use the old standbys - | sed/grep/find, etc. Could probably wrap it all up in `make` | or `just` if this is something you do regularly. | thangalin wrote: | It's not trivial. Some class names are substrings of | other classes. I'm pretty sure this requires loading and | parsing the source code's AST to accomplish without | breaking the build. The SO thread goes into more gnarly | details. | dcminter wrote: | Edit: Never mind, whartung enlightened me in a sibling | comment. Leaving the egg on my face for posterity below. | | --- | | Why do you want to change all the files? The license only | says you have to change the names of files that you modify. | It's a pretty weird term. | | If for some reason you were determined to change the names | of all files then you ought to be able to create a plugin | that walks the tree of classes and invokes the refactoring | capabilities of the IDE to make the change. It's a strange | use-case though so I'm not really surprised there isn't a | ready tool for it. | thangalin wrote: | > Why do you want to change all the files? | | Ever pull on a thread to unravel an entire sweater? | | Imagine wanting to add an SVG typesetter. To do this, the | interface implemented by the DVI typesetter needs to be | extracted and generalized so that it works for both SVG | and DVI (to maintain existing behaviour). The | `Typesetter` interface is used by, for example, | `BaseNode`, so any changes to that interface could touch | `BaseNode`. Changes to `BaseNode` can affect all `*Node` | classes, which then cascade over to `*Noad` and `*Prim` | classes, and from there the rest of the source base. | bedatadriven wrote: | Looks like it was inherited from an upstream project that has a | long history: https://github.com/jamespfennell/new-typesetting- | system#lice... | thangalin wrote: | I've been looking high-and-low for a way to rename Java classes | en masse without breaking the build. | | https://stackoverflow.com/questions/75154524/bulk-rename-of-... | | A number of classes have been renamed to have the Kt prefix. | There are other changes that don't yet have the new prefix | applied because changing those classes would ripple into, | essentially, the entire code base. Hence the search for a mass | rename procedure that works. Suggestions welcome. | | Donald Knuth set that rule up so that "plain.tex" would be the | same, world-wide. I don't know why the authors applied the same | license to the entire Java application, rather than limit it to | Knuth's work. | | I've also added a comment "// Originally" that indicates the | original name of the class, rather than the name of the file. | This minor license violation keeps its spirit if not its | letter. Otherwise there'd be no way to extract an inner class. | counttheforks wrote: | If you're fine using intellij then you can probably use its | scripting to do this. It has access to most of the same API | as plugins and can access intellij features such as its | refactor rename. | | The (meager) docs on scripting: | https://www.jetbrains.com/help/idea/ide-scripting- | console.ht.... The plugin docs & intellij source code | (especially the unit tests) are a better source of | information. | | And here's a script that will use intellij's refactoring to | rename all classes recursively in a directory to add the | prefix Renamed, correctly dealing with references & renaming | files if necessary: | @file:Suppress("NAME_SHADOWING") import | com.intellij.notification.Notification import | com.intellij.notification.NotificationType import | com.intellij.notification.Notifications import | com.intellij.openapi.actionSystem.* import | com.intellij.openapi.keymap.KeymapManager import | com.intellij.openapi.command.WriteCommandAction import | com.intellij.psi.* import com.intellij.psi.search.* | import com.intellij.refactoring.rename.RenameProcessor | import com.intellij.util.ThrowableConsumer import | java.io.PrintWriter import java.io.StringWriter | import javax.swing.KeyStroke // Usage: In IDEA: | Tools -> IDE Scripting Console -> Kotlin // Ctrl+A, | Ctrl+Enter to run the script // Select folder | containing target classes, Ctrl+Shift+A to open action menu, | search for Bulk refactor //<editor-fold | desc="Boilerplate"> val b = bindings as Map<*, *> | val IDE = b["IDE"] as com.intellij.ide.script.IDE | fun registerAction( name: String, keyBind: | String? = null, consumer: | ThrowableConsumer<AnActionEvent, Throwable> ) { | registerAction(name, keyBind, object : AnAction() { | override fun actionPerformed(event: AnActionEvent) { | try { consumer.consume(event); } | catch (t: Throwable) { val sw = StringWriter() | t.printStackTrace(PrintWriter(sw)) | log("Exception in action $name: $t\n\n\n$sw", | NotificationType.ERROR) throw t } | } }); } fun registerAction(name: | String, keyBind: String? = null, action: AnAction) { | action.templatePresentation.text = name; | action.templatePresentation.description = name; | KeymapManager.getInstance().activeKeymap.removeAllActionShort | cuts(name); | ActionManager.getInstance().unregisterAction(name); | ActionManager.getInstance().registerAction(name, action); | if (keyBind != null) { | KeymapManager.getInstance().activeKeymap.addShortcut( | name, | KeyboardShortcut(KeyStroke.getKeyStroke(keyBind), null) | ); } } fun log(msg: String, | notificationType: NotificationType = | NotificationType.INFORMATION) { log("Scripted | Action", msg, notificationType) } fun log( | title: String, msg: String, notificationType: | NotificationType = NotificationType.INFORMATION ) { | Notifications.Bus.notify( Notification( | "scriptedAction", title, msg, | notificationType ) ) } | //</editor-fold> registerAction("Bulk refactor") | lambda@{ event -> val project = event.project ?: | return@lambda; val psiElement = | event.getData(LangDataKeys.PSI_ELEMENT) ?: return@lambda | log("Bulk refactor for: $psiElement") WriteComma | ndAction.writeCommandAction(event.project).withGlobalUndo().r | un<Throwable> { psiElement.accept(object : | PsiRecursiveElementWalkingVisitor() { override | fun visitElement(element: PsiElement) { | super.visitElement(element); if (element !is | PsiClass) { return } | if(element.name?.startsWith("Renamed") == false) { | log("Renaming $element") // arg4 = | isSearchInComments // arg5 = | isSearchTextOccurrences val processor = | object : RenameProcessor(project, element, "Renamed" + | element.name, false, false) { override fun | isPreviewUsages(usages: Array<out UsageInfo>): Boolean { | return false } } | processor.run() } } }) | } } | | If your project is large this will temporarily freeze | intellij since it doesn't properly run on the background | thread but just give it a minute. | | By the way, this should work on all languages that IntelliJ | has a language plugin for, not just Java. | | Edit: Modified script to override isPreviewUsages to prevent | intellij from opening confirmation dialogs | thangalin wrote: | This looks rather promising. Doesn't work in my version of | IDEA (Build #IC-223.8214.52, built on December 20, 2022, | Community Edition), due to the following errors when | running the script: | | > Argument for @NotNull parameter 'module' of | org/jetbrains/.../...GroovyRunner must not be null. | | The project doesn't have any references to the NotNull | annotation, and there are a number of what appear to be | compile errors: | | https://i.ibb.co/dfxbV4p/rename-script.png | | Running with Ctrl+Enter produces: > | @file:Suppress("NAME_SHADOWING") | MultipleCompilationErrorsException: startup failed: | Script1.groovy: 1: Unexpected input: '@file:' @ line 1, | column 6. @file:Suppress("NAME_SHADOWING") | ^ 1 error | | I removed the line and re-ran, which resulted in: | > import com.intellij.notification.Notification [22 | ms]=> null | | Then I selected the entire text and pressed Ctrl+Enter | again, which returned: | MultipleCompilationErrorsException: startup failed: | Script4.groovy: 20: Unexpected input: '<*' @ line 20, | column 25. val b = bindings as Map<*, *> | ^ 1 error | | After changing the asterisks to question marks, the script | failed to run, indicating: | MultipleCompilationErrorsException: startup failed: | Script5.groovy: 25: Unexpected input: 'registerAction(\n | name: String,\n keyBind: String? =' @ line 25, column | 22. keyBind: String? = null, | ^ 1 error | | I appreciate the effort, though! | counttheforks wrote: | Make sure you pick Kotlin for the scripting language, not | Groovy. | | Then run the script like this: | | 1. In IDEA: Tools -> IDE Scripting Console -> Kotlin | | 2. Paste in the script | | 3. Ctrl+A, Ctrl+Enter to load the script. This should | show a green "Loaded!" notification | | 4. Select a folder containing the classes in the Project | window | | 5. Press Ctrl+Shift+A to open action menu | | 6. Search for Bulk refactor and select it | | Also please note the first version of the script had a | bug that would open confirmation dialogues for some | refactorings, so see the edited script. | thangalin wrote: | > 1. In IDEA: Tools -> IDE Scripting Console -> Kotlin | | No such sub-menu of Tools exists, but that's fine because | I've mapped Ctrl+Shift+A to bring up the menu. I | installed the Kotlin plugin and tried again. There was a | missing import. After fixing the missing import: | [732 ms]=> null | | I switched "Rename" to "Kt", of course, and re-ran the | script. | | > 4. Select a folder containing the classes in the | Project window | | > 5. Press Ctrl+Shift+A to open action menu | | > 6. Search for Bulk refactor and select it | | Opening the menu with Ctrl+Shift+A doesn't show the bulk | refactor script, but that could be because I remapped | Ctrl+Shift+A and am using the NetBeans keyboard mappings, | rather than the IntelliJ map. | | https://i.ibb.co/5LTsffj/bulk-refactor.png | counttheforks wrote: | > There was a missing import | | Oops :) | | When pressing Ctrl+Enter, did you see the green "Loaded!" | notification in the bottom right? If so, then I guess | installing the Kotlin plugin worked. | | > Opening the menu with Ctrl+Shift+A doesn't show the | bulk refactor script | | The Ctrl+Shift+A menu from the default keymap that I'm | talking about is the "Actions" dialog. You can also get | there via "Navigate -> Search Everywhere" and then | selecting the "Actions" tab (or just searching in All). | | Also you only have to select a single folder/package and | it will be processed recursively. I did not test what | happens when you select multiple folders. Might be fine, | might explode. | | edit: Just realized the script I pasted here does not | emit the "Loaded!" log message on load. So disregard | those comments. If you want to verify it loads correctly, | add this as the last line: | log("Loaded!") | | edit: HN rate limited my account again, so I'm not | allowed to post any new comments for the next few hours. | Good luck! | | If you do wind up using this script and want to modify | it, it is helpful to read | https://plugins.jetbrains.com/docs/intellij/psi.html & to | install the PsiViewer plugin from the marketplace. | layer8 wrote: | Ignoring the case of the TFA license that actually requires | you to rename the files, renaming packages is normally | enough, and is rather straightforward, which is probably why | there isn't much existing tooling for batch-renaming classes. | patrec wrote: | Wouldn't something like the following shell one-liner rename | get you quite close? export javas=$(find . | -regex '.*\.java$'); for old in $(grep -hrPo 'class | (?!Kt)\K[A-Z]\w+'); do echo $old; sed -i | "s/\<$old\>/Kt$old/g" $javas; rename "s/^$old\$/Kt$old/" | $javas; done | | Untested, unoptimized, needs gnu grep and perl's rename | utility (variously packaged as prename or rename-files, as | opposed to the crappy linux-utils rename). | patrec wrote: | Here you are, "gradle build" works with this cleaned up and | faster version (and the diff looks plausible on first | sight): javas=$(find . -regex '.*\.java$') | sed -i -E "$(printf 's/\\<(%s)\\>/Kt\\1/g;' $(grep -hrPo | '\b(class|interface|record|enum) | (?!Kt)(?!List\b)(?!Entry\b)\K[A-Z]\w+'))" $(echo $javas); | rename 's;\b(?!Kt)(\w+[.]java)$;Kt$1;' $(echo $javas) | | (The $(echo $javas) is so it works in both bash and zsh). | brazzy wrote: | > in Java, the name of the file is the name of the class. You | can't have the file name differ from the class inside. | | Actually, I'm fairly sure you can. You just need a custom | classloader. | brabel wrote: | Class files don't actually care about file names so | ClassLoader has nothing to do with this. The OP problem is | the source. The Java compiler won't compile a source file if | none of the classes (and the only public one) in the file | matches the name of the file. | rowls66 wrote: | Yes, commonly used ClassLoaders do care about file names | and directories. The Java compiler javac does not. The | javac compiler compiles compilation units which are | generally files, but name does not matter. | teraflop wrote: | No, the file names of compilation units _do_ matter to | javac. To quote the Java Language Specification, section | 7.6: | | > If and only if packages are stored in a file system | (SS7.2), the host system may choose to enforce the | restriction that it is a compile-time error if a class or | interface is not found in a file under a name composed of | the class or interface name plus an extension (such as | .java or .jav) if either of the following is true: | | > * The class or interface is referred to by code in | other ordinary compilation units of the package in which | the class or interface is declared. | | > * The class or interface is declared public (and | therefore is potentially accessible from code in other | packages). | | and javac does indeed enforce this restriction, at least | for public classes. It's an error to declare a public | class called Foo in a file that's called anything except | Foo.java. | brazzy wrote: | > Class files don't actually care about file names | | But URLClassLoader does, which definitely has something to | do with this. | | True, before you get there, you'd first need it to compile | - but modern JDKs have a compiler API that allows | customization via the StandardJavaFileManager interface, | and that looks like it should be able to do the job to make | source file names follow a different convention. | ebruchez wrote: | As a workaround, you could distribute the modified Foo.java as | Foo2.java, and have your build system rename Foo2.java to | Foo.java at build time. It might be all it takes (but IANAL). | mqus wrote: | WHich would completely cripple IDEs or at the very least | makes it difficult to work with | svat wrote: | For some history/context on what this is based on: | | * During 1977-78, Knuth wrote the first version of TeX (now | called TeX78) in SAIL and gave a talk[1] about it. Everyone was | excited, and lots of people at other universities/labs starting | writing their own ports of TeX. | | * During 1980-82, Knuth set out to rewrite a "portable" TeX using | Pascal, the most widely available language at that time (actually | he wrote his own macros/wrapper on top of Pascal; the resulting | language is called WEB). This (TeX82 aka TeX) is the program | still in use today if you invoke "tex". (Continues to be updated | once every few years, but basically stable.) | | * During 1998-2000, there was a Java rewrite of TeX called the | New Typesetting System (NTS)[2], which had an alpha release and | then was basically abandoned: it had all the problems of TeX and | was several times slower, so it was regarded by many as a | failure. Meanwhile extensions of TeX like pdfTeX came into | existence, which are the programs most people use today. | | * The code still exists though, and I imagine people must have | tried it on and off. But since I can't find any earlier record, | I'll provisionally take credit for the idea of | resurrecting/resurfacing it, when I tried it in 2017: [3] | | * Recently (in the last few months), Dave Jarvis (user | `thangalin` here) has forked/modernized this Java implementation | and is trying it out. (I noticed when he left his comment on my | TeX.SE question; I guess his post | https://tex.stackexchange.com/a/672614 on another question is | what led to it being posted here.) I'm excited about this; | looking forward to see how it gets used. | | (Apologies if this long comment potentially takes attention away | from the current state and future possibilities, but felt like | the history is useful context...) | | -------- | | I'll also add some history about the license, as I see another | thread about it. When Knuth wrote TeX, it was to solve a specific | problem he had encountered in publishing: the second edition of | TAOCP Vol 2 was turning out worse in appearance than the first, | because the publisher was moving from hot-metal typesetting (with | Monotype machines) to phototypesetting, and could not achieve | exactly the same appearance. While being bothered by this, Knuth | discovered that digital typesetting machines were starting to | come into existence: now it was just 0s and 1s, and as a | programmer he felt he could handle it. That was his motivation | for creating TeX and METAFONT: the appearance of each page would | remain reproducible forever into the future. | | Except: when he publicized TeX and various clones sprung up, the | situation was heading towards merely re-introducing the problem: | if different computer systems had different programs all called | "tex" that made even subtly different choices about line-breaking | or whatever, then someone could write a paper/book on one | computer, fine-tune the typesetting, then send the .tex file to | their colleague / editor / publisher who used another computer, | and no longer be confident that they would get the same result. | This was one of the reasons he used the rewrite to create a | portable TeX, going to great pains to use only a common subset of | Pascal (Pascal had widely diverging compiler and language | features), wrote a very demanding conformance test suite called | the TRIP test[4] and insisted that any program would need to pass | this test to be called "tex" -- that way, everyone using "tex" | could be sure they were writing something that would always | produce the same result everywhere. If you wanted to make | different choices in the program you could, as long as you | changed the name. | | When he announced in 1990[5] that TeX would be stable (no more | features), this is what he wrote: | | > _My work on developing TeX, METAFONT, and Computer Modern has | come to an end. I will make no further changes except to correct | extremely serious bugs. I have put these systems into the public | domain so that people everywhere can use the ideas freely if they | wish. I have also spent thousands of hours trying to ensure that | the systems produce essentially identical results on all | computers. [...]_ | | > _[...] anybody can make use of my programs in whatever way they | wish, as long as they do not use the names TeX, METAFONT, or | Computer Modern. In particular, any person or group who wants to | produce a program superior to mine is free to do so. However, | nobody is allowed to call a system TeX or METAFONT unless that | system conforms 100% to my own programs [...]_ | | > _I welcome continued research that will lead to alternative | systems that can typeset documents better than TeX is able to do. | But the authors of such systems must think of another name. That | is all I ask, after devoting a substantial portion of my life to | the creation of these systems and making them available to | everybody in the world. I sincerely hope that the members of TUG | will help me to enforce these wishes..._ | | etc. And NTS derives a similar licence, and this project (being | forked from NTS) simply carries over the license. | | Of course such a license is unusual today, and thousands of | pages' worth of ink have been spilled over whether or not this | counts as "free software" (predating GPL / MIT license etc), and | (finally) grudgingly accepted to be. You can read about the | history of the LaTeX project's similar license (LPPL) and all the | debates on Debian/FSF mailing lists etc if you're morbidly | interested[6]; the discussion usually proceeds on similar lines | and I hope it doesn't get repeated here. :-) | | [1]: Gibbs lecture, "Mathematical Typography": | https://doi.org/10.1090/S0273-0979-1979-14598-1 | | [2]: | https://en.wikipedia.org/w/index.php?title=New_Typesetting_S... | | [3]: https://tex.stackexchange.com/questions/385645/is-nts-new- | ty... | | [4]: http://mirrors.ctan.org/info/knuth-pdf/tex/tripman.pdf | | [5]: https://www.tug.org/TUGboat/tb11-4/tb30knut.pdf | | [6]: https://www.tug.org/TUGboat/tb32-1/tb100mitt.pdf | TylerE wrote: | Aren't pretty much all modern TeX distorts using luatex? | dhosek wrote: | Not really. The luaTeX engine is the slowest and some of its | defaults differ from the behavior of pdfTeX and XeTeX. I | generally recommend people use XeTeX (which is also slower | than pdfTeX but faster than luaTeX), unless either they | specifically need some luaTeX facility or they are required | to use pdfTeX (some publishers insist on pdfTeX, although | their number is declining. I think that arxiv used to (maybe | still does) be one site that only supported pdfTeX, but I | could be wrong about that. ___________________________________________________________________ (page generated 2023-01-23 23:00 UTC)