This project attempts to create a completely static website of your source code, that navigates all the way down to the depths of the JDK. The goal is to navigate as well as Intellij. Try it yourself by navigating from Apache Commons Text's StringEscapeUtils.
I used grepcode at least once a week and I am sad to see it go. It's navigation was almost as good as an IDE. You could navigate from apache commons all the way down to the JDK. Occasionally, I would forget what I was originally doing and get lost in the depths of the JDK. I hope that I can create a spiritual successor by using this tool, that will live much longer since the source code is available, and the resulting static web pages are decentralized.
- Each page is static
- Works well without javascript
- Find a project you want to browse from: jdk8, jdk11, jdk17, Apache Lang3, Apache Collections, Apache Text, Github Java Parser OdinCodeBrowser
- Find your class (ex: A class Odin uses StringEscapeUtils)
- Start reading code and navigating by clicking on the links, which are underlined.
git clone git@github.com:josephmate/OdinCodeBrowser.git
cd OdinCodeBrowser
args="--inputSourceDirectory <path_to_your_source>"
args="$args --outputDirectory <output_directory>"
args="$args --webPathToCssFile https://josephmate.github.io/OdinCodeBrowser/css/styles.css"
args="$args --webPathToSourceHtmlFiles <directory_on_webserver>"
args="$args --languageLevel JAVA_16"
args="$args --urlToDependantIndexJson <url_of_dependencies_index_json_file>"
mvn install exec:java \
-Dexec.mainClass=Main \
-Dexec.args="$args"
Creating an Odin Navigator for my project as an example, with dependencies on github javaparser, apache commons, apache commons text, and JDK8 that I host on https://josephmate.github.io/OdinCodeBrowser/ .
args="--inputSourceDirectory src/main/java"
args="$args --outputDirectory docs/odin.code.browser"
args="$args --webPathToCssFile /OdinCodeBrowser/css/styles.css"
args="$args --webPathToSourceHtmlFiles /OdinCodeBrowser/odin.code.browser"
args="$args --languageLevel JAVA_16"
args="$args --urlToDependantIndexJson https://josephmate.github.io/OdinCodeBrowserJdk8/index.json"
args="$args --urlToDependantIndexJson https://josephmate.github.io/OdinCodeBrowserRepos/javaparser-core_3.23.1/index.json"
args="$args --urlToDependantIndexJson https://josephmate.github.io/OdinCodeBrowserRepos/commons-lang3_3.11/index.json"
args="$args --urlToDependantIndexJson https://josephmate.github.io/OdinCodeBrowserRepos/commons-text_1.9/index.json"
args="$args --urlToDependantIndexJson https://josephmate.github.io/OdinCodeBrowserRepos/commons-collections_4.4/index.json"
mvn install exec:java \
-Dexec.mainClass=Main \
-Dexec.args="$args"
- Start at a random class in jdk8
- Using links only, how fast can you get my favourite class: HashMap?
For example I did I18NImpl -> PropertyResourceBundle -> HashMap
However, sometimes you'll pick a class that does not have a path to HashMap :(.
Your Java IDE can do a much better job. As I write this project, I use Intellij to navigate from my project's code to the code of the projects I depend on. The experience is so much better in the IDE and you should use that. If you are already using your IDE, keep using it!
If you don't use an IDE then github provides some support for navigating code without any extra effort. Just commit your code as you normally do and github updates the navigation.
The two features together that sets Odin apart from an IDE and GitHub:
- sharing links to the code (can't do this in an IDE). Since it's a web page, this means you can use it on low memory devices like your phone!
- can link to the dependant sources as well (github can't do this)
Dimension | Odin | IDE | Github |
---|---|---|---|
Shares links | ✅ | ❌ | ✅ |
Minimal CPU Usage | ✅ | ❌ | ✅ |
Minimal Memory Usage | ✅ | ❌ | ✅ |
Minimal Storage Usage | ✅ | ❌ | ✅ |
Realtime | ❌ | ✅ | ❌ |
Automatically Applied | ❌ | ✅ | ✅ |
Static Pages | ✅ | ? | |
Works without javascript | ✅ | ❌ | |
Navigate to dependencies | ✅ | ✅ | ❌ |
Comment on code | ❌ | ✅ | ✅ |
Navigation Complete | ❌ | ✅ | ❌ |
Explanation of criteria:
- Shares links: In Odin and GitHub you can share links to any line in the code. An IDE does not let you do that.
- Minimal CPU/Memory/Storage Usage: Since Odin and GitHub are not realtime, no cpu, memory or disk space is needed by the client to efficiently calculate all the indexes.
- Realtime As you make changes in an IDE, the indexes are updated and you can navigate to the new code changes or new dependencies. For GitHub you need to commit and push before the navigation updates. For Odin you need to run this tool and publish the generated html files.
- Automatically Applied: By rebuilding in the IDE, new code is recognized. By committing and pushing your changes to GitHub, the navigation is updated. For Odin, you must manually build and publish the static web pages. You unfortunately need to manage this. GitHub and an IDE manage this for you with no effort on your part.
- Static Pages: Odin's output is hosted as static web pages that can be efficiently distributed. This comparison doesn't make sense for an IDE.
- No javascript: Odin doesn't use javascript while GitHub needs to.
- Navigate to dependencies: IDE and seamlessly navigate to your dependencies. Odin plans to navigate to dependencies when you build. It will not be as convenient as an IDE. GitHub only lets you navigate within the same repository.
- Navigation Complete: anything you could possibly want to navigate to or from in an IDE works. Odin is working on matching an IDE and might reach the same level one day. GitHub navigation is okay. It gives you a list of options as a pop. Odin tries to be like an IDE and take you directly to the definition of the variable or method without asking you which one. For now, Odin sometimes makes mistakes due to method overloading.
- Comment on code: GitHub is probably the best for commenting and discussing code. You can also do this in an IDE with plugins, but it's not as convenient as GitHub. Since Odin pages are static without javascript, it's impossible to have commenting.
These are alternatives that I do not use on a daily basis so I do not feel qualified in giving a thoughtful review. However, I'm going to help you find what you're looking for.
woboq : Looks exactly what I'm trying to achieve, but for C++ instead of Java. If you're working on open source C/C++ , I recommend checking it out.
Opengrok: Provides much better searching than what Odin can provide. However, as a result some components cannot be hosted as static files. If you host Opengrok for your project, I recommend it.
Sourcegraph: The search is really good. It like a super powered Github search. However, they aren't indexing OdinCodeBrowser yet.. I'm not sure what triggers the indexing because some of my old side projects are there. Also, it does not support cross repo navigation.
- I use Github's JavaParser to visit nodes in the Java AST.
- Through visiting, I build indexes of classes, their methods, their fields, and their super classes.
- Through one more visit, I look to see if there's anything in the index I can create navigation links to and place these into a 'Rendering Queue'.
- I iterate over the code char by checking if I need to insert anything.
- All of these are saved as HTML files.
- The index is saved as a json file for other repos to use. That way they do not have to checkout the source code and build the index of all the dependencies. It also allows the repositories to independently host their source code and allows dependents to navigate from their code host on their servers to the code hosted on the dependency's servers.
The color scheme is based on vim-dichromatic put together by Romain Lafourcade. I wanted a single color scheme that could be used by anyone, since it is really difficult for readers to change the styles. In order for someone to change the style, they will need to install a plugin for their browser and override Odin's css styles. Maybe in the future there will be some javascript to select from a list of styles that writes to your browser's local storage.
Below is a checklist of features Odin needs.
- Line numbers with links
- Code is easily copy and pastable
- Code is copy and pastable
- Copied code is exactly the same as pasted code
- Click on a Type to navigate to the definition of that type
- Type
- Interface
- Annotation
- Outer class
- Inner class
- Enum
- Record
- Generics
- Class inside function
- Anonymous class
- Type Usage
- Variable type
- Method return type
- Extends type
- Implement type
- Record
- Enum
- Generics
- Annotation (the implementation of the annotation)
- Import
- Types through wildcard imports (ex: List from java.util.*)
- Types within same package (ex: ThreadPoolExecutor when in package java.util.concurrent)
- Types from java.lang (ex: IllegalArgumentException)
- Types within same file
- Type
- Click on method to go the definition of that method
- Static and only one function with the name (ex: Objects.hashCode(key))
- Functions within the same file (ex: putMapEntries(m, true)
- Functions on this (ex: this.getCanonName())
- Object instance and only one function with the name and not in super class (ex: x.getClass())
- Object instance and only one function with the name but in some super class
- super.method() (super.clone() in HashMap)
- Chained method calls
- Functions are overloaded (different parameters)
- all arguments are variables (ex: String.lastIndexOf(char vs String))
- length heuristic for generics (ex: Pair.of(A,B) vs Pair.of(Map.Entry<A,B>))
- all args are variables or literals (ex add(a, 1))
- where any of the arguments are expressions (ex: add(add(1,2), add(a,b)))
- variable arguments (ex: int add(int... vals))
- import static functions
- Record getter functions
- Super constructors
- Overloaded constructors
- Scoping rules (if names are duplicated in multiple scopes, need to use the closest scope)
- String literal method calls (ex: "true".equals(blah))
- class literal method calls (ex: boolean.class.getName())
- Click on variable to the definition of that variable
- from function param
- local var
- from scope (for/while/if)
- field var
- static field var
- Enum value
- this.field
- record.value
- static variable (ex: System.out)
- variable chaining (ex: a.b.c.d)
- Click on method to get a list of implementations
- Click on Override takes you to nearest super class's method that was overridden
- File list in root directory /OdinCodeBrowserJdk8
- Click on class definition to get all usages
- Click on method definition to get all usages
- Click on variable definition to get all usages
- Click on class/interface definition to get all subtypes
- Click on method definition to get all overrides and impls
- List of functions and fields at the side of the page.
- List of functions within this class, grouped by visibility
- List of fields within this class
- List of functions and fields from super classes
- Multi repository support (ex: browsing guava but also linking to JDK8)
- Repository exposes it's index file as json
- Build loads index files, then builds it own index
- Create a demo using current project OdinCodeBrowser -> apache text -> apache commons -> JDK8
- Take a look at SourceHtmlRenderer
- In there I use apache commons text to handle the html escaping for me
- From SourceHtmlRenderer can navigate to the StringEscapeUtils.escapeHtml4 implementation
- escapeHtml4 uses CharSequenceTranslator.translate
- Ignore not being able to navigate to the overloaded method. Odin doesn't support that yet.
- Validate.isTrue which is in apache commons lang3
- Again ignore that it went to the wrong overload of Validate.isTrue
- Which throws IllegalArgumentException which allows you to navigate to jdk8!
- Nice syntax highlighting somehow without javascript!
- Make it look decent on mobile (IE: not tiny text)
- Some syntax highlighting
- A dark mode syntax highlighting on
- pick a colour scheme: vim-dichromatic
- comments
- literals
- strings
- numbers
- boolean
- enum values
- native types (boolean, char, int, etc.)
- keywords (public, static, record, class, etc)
- this
- super
- class + modifiers
- modifiers of constructors
- modifiers of fields
- modifiers of functions
- modifiers of params
- modifiers of locals
- for
- synchronized
- import
- switch case
- if
- while
- do
- return
- extends
- implements
- Types
- function calls
- variables
- Render javadoc as text like in Intellij
- Link to native code
- navigation comparison between Odin, IDE, and GitHub
- Figure out how to support repos with multiple source directories
- Figure out a better release process other than having a developer clone the project and use the
mvn exec:java
goal.- Tried fatJar but does not work well with java 16