yveszoundi / eglot-java Goto Github PK
View Code? Open in Web Editor NEWJava extension for the eglot LSP client
License: GNU General Public License v3.0
Java extension for the eglot LSP client
License: GNU General Public License v3.0
There appears to be no "official" way to upgrade the installed JDT LS. You could delete the installation directory, but it's obviously a hack. Probably also need to shut down (how exactly?) the eglot server and (manually?) start again after deleting.
eglot-java-mode
, ensure that the eglot-server is not running, otherwise stop it: Maybe just disabling eglot-java-mode
is good enoughjdtls
version to a temporary folderjdtls
installation folder to a temporary folderjdtls
to its new destinationjdtls
installation folderjava-mode
, ensure that the eglot-server is running, by reassociating the buffer with eglot-java-mode
This depends on #15 .
Currently it's only possible to run tests for an entire class.
The eglot-java-run-test
function behavior will be changed:
There's no built-in LSP server handler that will return automatically all the available test methods in a given file.
If the mouse cursor is near a helper method, this package will blindly assume that "you know what you're doing" and you'll get an error message from the Junit console logs.
I have been looking at an issue over in the JDT LSP repository: eclipse-jdtls/eclipse.jdt.ls#2322
A summary of how I reproduce this issue (with the latest snapshot of jdtls, not using eglot-java at all)
eglot-server-programs
: (java-mode . ("jdtls" :initializationOptions (:extendedClientCapabilities (:classFileContentsSupport t))))
groot/src/main/java/org/jlab/groot/math/F1D.java
xref-find-definitions
on the Expression
symbol on line 23The relevant eglot logs
(:jsonrpc "2.0" :id 8 :method "textDocument/definition" :params
(:textDocument
(:uri "file:///home/user/dev/groot/src/main/java/org/jlab/groot/math/F1D.java")
:position
(:line 22 :character 4)))
[server-reply] (id:8) Sun Nov 13 13:37:57 2022:
(:jsonrpc "2.0" :id 8 :result
[(:uri "jdt://contents/exp4j-0.4.4.jar/net.objecthunter.exp4j/Expression.class?=groot/%5C/home%5C/user%5C/.m2%5C/repository%5C/net%5C/objecthunter%5C/exp4j%5C/0.4.4%5C/exp4j-0.4.4.jar=/maven.pomderived=/true=/=/javadoc_location=/jar:file:%5C/home%5C/user%5C/.m2%5C/repository%5C/net%5C/objecthunter%5C/exp4j%5C/0.4.4%5C/exp4j-0.4.4-javadoc.jar%5C!%5C/=/=/maven.groupId=/net.objecthunter=/=/maven.artifactId=/exp4j=/=/maven.version=/0.4.4=/=/maven.scope=/compile=/=/maven.pomderived=/true=/%3Cnet.objecthunter.exp4j(Expression.class" :range
(:start
(:line 24 :character 13)
:end
(:line 24 :character 23)))])
I think this package would be a good place to handle those JDT URIs. There is a reply in the issue over in JDTLS repository that explains the process for handling them. Essentially the client must make an extra request to the java/classFileContents
method of the language server, which will serve back the contents of the decompiled class. From there the client is free to dump the contents into a buffer.
I would be happy to take a swing at implementing this here at some point, but wanted to drop an issue here in case someone else wants to try and beat me to it.
I cannot imagine why it would work in some cases, but not in others, but throughout the majority of the project things work fine, until I see the @Getter
annotation.
Everything works fine when I compile the thing.
Also, my lombok verrsion (1.18.30) seems to work fine with lsp-mode
and nvim-jdtls.
When things go wrong, I get a lint error at the top of the buffer which reads:
Java [0]: Lombok annotation handler class lombok.eclipse.handlers.HandleGetter failed - See error log.
and sometimes, but not always, missing references down the buffer from there.
My vmargs are set as such:
(defun td-eglot-java-setup ()
(setq eglot-java-eclipse-jdt-args (list (concat "-javaagent:"
td-eglot-java-lombok-path)))
(td-bind-keys '(("C-c e n" . eglot-java-file-new)
("C-c e x" . eglot-java-run-main)
("C-c e t" . eglot-java-run-test)
("C-c e N" . eglot-java-project-new)
("C-c e T" . eglot-java-project-build-task)
("C-c e R" . eglot-java-project-build-refresh))
eglot-java-mode-map))
(add-hook 'eglot-java-mode-hook 'td-eglot-java-setup)
When I check the jdt args at run time, they look pretty good to me:
Its value is
("-javaagent:/home/trev/.emacs.d/eglot-java/lombok/lombok.jar" "-jar" "/home/trev/.emacs.d/share/eclipse.jdt.ls/plugins/org.eclipse.equinox.launcher_1.6.700.v20231214-2017.jar" "-configuration" "/home/trev/.emacs.d/share/eclipse.jdt.ls/config_linux" "-data" "/home/trev/.emacs.d/eglot-java-eclipse-jdt-cache/ba359c4c589a8cb98349d0107123787e")
Original value was nil
Any ideas as to what's going on here?
This key should be reserved for users and never be automatically bound.
Hi,
would you consider adding this package to ELPA? If so, may I suggest to get in touch with @phikal to check what you need to do?
Thank you!
When you invoke the eglot-java-run-test
function, it will attempt to download the maven junit-platform-console-standalone
jar artifact (if not already cached locally). You can describe the variable eglot-java-junit-platform-console-standalone-jar
for more details (C-h v
).
It is not current possible to systemically upgrade the install junit standalone console jar. This is similar in nature to issue #16 .
Check for the latest version and then upgrade any existing download.
It is possible that a version might denote a milestone (e.g., 1.0.0-M1
or 1.0.0-rc1
). It is not guaranteed that those versions will be recorded as "releases" in the maven repository metadata.xml
.
Core emacs functions will report 1.0.0-M1
as invalid, in comparison to 1.0.0
. => (version< "1.0.0-M1" "1.0.0")
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<groupId>org.junit</groupId>
<artifactId>junit-gradle</artifactId>
<versioning>
<latest>5.0.0-ALPHA</latest>
<release>5.0.0-ALPHA</release>
<versions>
<version>5.0.0-ALPHA</version>
</versions>
<lastUpdated>20160201140008</lastUpdated>
</versioning>
</metadata>
Let's assume that the latest available version is 1.0.0-M2
and the installed version is 1.0.0-M1
:
-
)(version<= "1.0.0" "1.0.0")
M2
with the installed version M1
string> "M2" "M1"
)Gradle added Kotlin support in late 2016, starting with version 3.0.
eglot-java
never included any Kotlin
support for Gradle projects for 2 main reasons:
Kotlin
professionally, but I did try it few times (mostly in its early days)Kotlin
As of mid-2023, it seems that Kotlin
is now the default DSL for Gradle project files.
There's no support for Gradle
Kotlin
files : no build.gradle.kts
or settings.gradle.kts
existence checks.
Some people might prefer Kotlin
to the Groovy
syntax, maybe even more likely when working on Android projects.
project.el
(see eglot-java--project-try
)eglot-java--project-gradle-p
)setttings.gradle.kts
if it exists (See eglot-java--project-name-gradle
)build.gradle.kts
file (See eglot-java-project-build-refresh
)When eglot tries to launch an LSP server, a timeout error can happen if the LSP server is not ready very quickly.
From a user perspective, the timeout error could be seen as a bug??
The documentation task seem like a better option. The user may want all the connectivity to be async with eglot-sync-connect
set to 0
.
Hi! Awesome package and much appreciated :)
Small tweaks here would make it perfect for my use cases:
These are small things... I am always wondered by the Emacs evolution in recent years!
I am currently in a Termux session so can't really try to open a PR easilly here but I might try it in the future. Leaving this here for reference if anyone can do it before me (which might take a while since I am no pro in Elisp).
Thanks once again for your work! Happy incoming new year!
There's currently no way to customize eglot
initialization options.
That would allow for example registering extended client capabilities such as classFileContentsSupport
in the context of Java support. It's probably best to focus only on extendedClientCapabilities
for now.
In order to support JDT URIs (#6), a custom AOP advice was introduced. This should not be required in future MELPA versions of eglot.
Remove the AOP advice around eglot URI functions (see TODO markers in eglot-java.el
).
While automatically installing the eclipse lsp works fine, there no longer is a eglot--eclipse-jdt or similar in eglot master, so starting it fails.
I am editing a simple Java for Android project, and (eglot-java-mode)
does not even start.
Error buffer:
[jsonrpc] D[09:52:40.631] Running language server: /usr/lib64/zulu-openjdk11/bin/java -Declipse.application=org.eclipse.jdt.ls.core.id1 -Dosgi.bundles.defaultStartLevel=4 -Declipse.product=org.eclipse.jdt.ls.core.product -jar /home/lockywolf/.emacs.d/share/eclipse.jdt.ls/plugins/org.eclipse.equinox.launcher_1.6.700.v20231214-2017.jar -configuration /home/lockywolf/.emacs.d/share/eclipse.jdt.ls/config_linux -data /home/lockywolf/.emacs.d/eglot-java-eclipse-jdt-cache/fb975d1c5daaeb77385e3b232729edd2
[jsonrpc] e[09:52:40.633] --> initialize[1] {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"processId":4795,"clientInfo":{"name":"Eglot","version":"1.15"},"rootPath":"/home/lockywolf/OfficialRepos/Telegram/TMessagesProj/","rootUri":"file:///home/lockywolf/OfficialRepos/Telegram/TMessagesProj","initializationOptions":{"extendedClientCapabilities":{"classFileContentsSupport":true},"workspaceFolders":["file:///home/lockywolf/OfficialRepos/Telegram/TMessagesProj"],"settings":{"java":{"home":"/usr/lib64/zulu-openjdk8"},"import":{"gradle":{"enabled":true},"wrapper":{"enabled":true}}}},"capabilities":{"workspace":{"applyEdit":true,"executeCommand":{"dynamicRegistration":false},"workspaceEdit":{"documentChanges":true},"didChangeWatchedFiles":{"dynamicRegistration":true},"symbol":{"dynamicRegistration":false},"configuration":true,"workspaceFolders":true},"textDocument":{"synchronization":{"dynamicRegistration":false,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":false,"completionItem":{"snippetSupport":true,"deprecatedSupport":true,"resolveSupport":{"properties":["documentation","details","additionalTextEdits"]},"tagSupport":{"valueSet":[1]}},"contextSupport":true},"hover":{"dynamicRegistration":false,"contentFormat":["markdown","plaintext"]},"signatureHelp":{"dynamicRegistration":false,"signatureInformation":{"parameterInformation":{"labelOffsetSupport":true},"documentationFormat":["markdown","plaintext"],"activeParameterSupport":true}},"references":{"dynamicRegistration":false},"definition":{"dynamicRegistration":false,"linkSupport":true},"declaration":{"dynamicRegistration":false,"linkSupport":true},"implementation":{"dynamicRegistration":false,"linkSupport":true},"typeDefinition":{"dynamicRegistration":false,"linkSupport":true},"documentSymbol":{"dynamicRegistration":false,"hierarchicalDocumentSymbolSupport":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]}},"documentHighlight":{"dynamicRegistration":false},"codeAction":{"dynamicRegistration":false,"resolveSupport":{"properties":["edit","command"]},"dataSupport":true,"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}},"isPreferredSupport":true},"formatting":{"dynamicRegistration":false},"rangeFormatting":{"dynamicRegistration":false},"rename":{"dynamicRegistration":false},"inlayHint":{"dynamicRegistration":false},"publishDiagnostics":{"relatedInformation":false,"codeDescriptionSupport":false,"tagSupport":{"valueSet":[1,2]}}},"window":{"showDocument":{"support":true},"workDoneProgress":true},"general":{"positionEncodings":["utf-32","utf-8","utf-16"]},"experimental":{}},"workspaceFolders":[{"uri":"file:///home/lockywolf/OfficialRepos/Telegram/TMessagesProj","name":"~/OfficialRepos/Telegram/TMessagesProj/"}]}}
[stderr] Picked up _JAVA_OPTIONS: -Dsun.java2d.uiScale=1.35 -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true -Dswing.defaultlaf=com.sun.java.swing.plaf.gtk.GTKLookAndFeel -Dswing.crossplatformlaf=com.sun.java.swing.plaf.gtk.GTKLookAndFeel
[stderr] Jan 19, 2024 9:52:41 AM org.apache.aries.spifly.BaseActivator log
[stderr] INFO: Registered provider ch.qos.logback.classic.servlet.LogbackServletContainerInitializer of service jakarta.servlet.ServletContainerInitializer in bundle ch.qos.logback.classic
[stderr] Jan 19, 2024 9:52:41 AM org.apache.aries.spifly.BaseActivator log
[stderr] INFO: Registered provider ch.qos.logback.classic.spi.LogbackServiceProvider of service org.slf4j.spi.SLF4JServiceProvider in bundle ch.qos.logback.classic
[jsonrpc] D[09:52:41.541] Connection state change: `exited abnormally with code 13
'
----------b---y---e---b---y---e----------
[stderr]
[stderr]
[stderr] nil
[stderr] nil
[stderr] Process EGLOT (TMessagesProj/(java-mode java-ts-mode)) stderr finished
Since 2017-2018 (before the code was even on GitHub), eglot-java
always had very minimal configuration options for the Eclipse JDT language server. It might be time to add few settings found in forks.
It's not possible to manual define the following
java
executable, if the java
executable is not already in the user PATH
environment variableNow that with eglot-java it's possible to easily run main classes and test classes/methods, I would like to understand how it could support specific configurations for such runs.
The following come to mind:
One specific case is for projects that use Lombok. With the following in .dir-locals.el
we can convince jdtls
to process Lombok's annotations:
((nil . ((eval . (setq-local eglot-java-eclipse-jdt-args
(push (concat "-javaagent:" "/PATH/TO/lombok.jar")
eglot-java-eclipse-jdt-args))))))
I wonder whether .dir-locals.el
is a good mechanism to set Java run configurations in general. For main class args that doesn't seem very convenient, but for environment vars and JVM args it may be.
What are you thoughts?
Eglot and eglot-java didn't start when I open any file with java source code from my project.
Steps to reproduce:
File mode specification error: (wrong-type-argument consp nil)
Emacs 28.2
Eglot 1.10
Eglot-java 20221218.1809 from MELPA
ะั configuration:
(use-package eglot)
(use-package eglot-java
:requires eglot
:config
(progn
(add-hook 'java-mode-hook 'eglot-java-mode)))
I found (via M-x toggle-debug-on-error
) what problem also reproduces, when I call M-x eglot-java-mode
after open the java file.
Debugger entered--Lisp error: (wrong-type-argument consp nil)
eglot-java--init()
eglot-java-mode(toggle)
funcall-interactively(eglot-java-mode toggle)
call-interactively(eglot-java-mode record nil)
command-execute(eglot-java-mode record)
execute-extended-command(nil "eglot-java-mode" "eglot-java-m")
funcall-interactively(execute-extended-command nil "eglot-java-mode" "eglot-java-m")
call-interactively(execute-extended-command nil nil)
command-execute(execute-extended-command)
When you invoke the eglot-java-run-test
function, it will attempt to download the maven junit-platform-console-standalone
jar artifact (if not already cached locally).
sonatype.org
repositories.eglot-java-junit-platform-console-standalone-jar-url
variable (C-h v
).When behind a VPN (e.g., NordVPN), sonatype.org
might request some user credentials for the junit jar download (as no browser captcha will be displayed to check whether you're a bot or a real human).
Please note that I'm not promoting a specific VPN such as NordVPN.
junit-platform-console-standalone
root maven url, such as https://repo1.maven.org/maven2/. If that URL creates problems, users can change it themselves with another URL that doesn't attempt to block them behind their particular network setup.eglot-java-junit-platform-console-standalone-jar-url
as obsolete (See the make-obsolete-variable
function)metadata > versioning > latest
).
metadata.xml
contents into a temporary buffer (with the url-insert-file-contents
function)XML_STRING
) by parsing the current temporary XML buffer(caddar (xml-get-children (car (xml-get-children (car (XML_STRING)) 'versioning)) 'release))
I have a failure with ensure-eglot
due to these lines. This can't be serialized to JSON because it's an invalid property list (a tag with 2 values).
Lines 146 to 149 in ea51c47
Moving a left parenthesis seems to fix it, though I'm just guessing that :import
is another setting of :java
.
`(:settings (:java (:home ,home)
:import (:gradle (:enabled t)))
)
https://discourse.doomemacs.org/t/error-when-loading-eglot-java/3923
For multibyte issues, not really sure about what can be done, buffer local settings would be set for a given file mode apparently.
For functions not automatically discovered, libraries can be eagerly loaded (xml
and tar-mode
libraries)
Emacs version 28.2
OS version: MacOS 13.3.1
Architecture: ARM (Apple M1)
(setq eglot-java-eclipse-jdt-args
'(
"-noverify"
"-Xmx1G"
"-XX:+UseG1GC"
"-XX:+UseStringDeduplication"
"-javaagent:/.m2/repository/org/projectlombok/lombok/1.18.20/lombok-1.18.20.jar"/.m2/repository/org/projectlombok/lombok/1.18.20/lombok-1.18.20.jar"
"-Xbootclasspath/a:
))
jsonrpc-request: jsonrpc-error: "request id=73 failed:", (jsonrpc-error-code . -32603), (jsonrpc-error-message . "Internal error."), (jsonrpc-error-data . "java.util.concurrent.CompletionException: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @377c7eaf
at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:315)
at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:320)
at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:649)
at java.base/java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:483)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @377c7eaf
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199)
at java.base/java.lang.reflect.Method.setAccessible(Method.java:193)
at org.eclipse.osgi.internal.loader.ModuleClassLoader.overrideLoadResult(ModuleClassLoader.java:86)
at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
at org.eclipse.jdt.internal.compiler.parser.Parser.consumeExitVariableWithInitialization(Parser.java:4205)
at org.eclipse.jdt.internal.compiler.SourceElementParser.consumeExitVariableWithInitialization(SourceElementParser.java:406)
at org.eclipse.jdt.internal.compiler.parser.Parser.consumeRule(Parser.java:7156)
at org.eclipse.jdt.internal.compiler.parser.Parser.parse(Parser.java:13179)
at org.eclipse.jdt.internal.compiler.parser.Parser.parse(Parser.java:13434)
at org.eclipse.jdt.internal.compiler.parser.Parser.parse(Parser.java:13391)
at org.eclipse.jdt.internal.compiler.SourceElementParser.parseCompilationUnit(SourceElementParser.java:1122)
at org.eclipse.jdt.internal.core.CompilationUnit.buildStructure(CompilationUnit.java:196)
at org.eclipse.jdt.internal.core.Openable.generateInfos(Openable.java:266)
at org.eclipse.jdt.internal.core.JavaElement.openWhenClosed(JavaElement.java:597)
at org.eclipse.jdt.internal.core.JavaElement.getElementInfo(JavaElement.java:328)
at org.eclipse.jdt.internal.core.JavaElement.getElementInfo(JavaElement.java:314)
at org.eclipse.jdt.internal.core.Openable.getBuffer(Openable.java:296)
at org.eclipse.jdt.ls.core.internal.JDTUtils.findElementsAtSelection(JDTUtils.java:992)
at org.eclipse.jdt.ls.core.internal.JDTUtils.findElementAtSelection(JDTUtils.java:978)
at org.eclipse.jdt.ls.core.internal.handlers.NavigateToDefinitionHandler.computeDefinitionNavigation(NavigateToDefinitionHandler.java:83)
at org.eclipse.jdt.ls.core.internal.handlers.NavigateToDefinitionHandler.definition(NavigateToDefinitionHandler.java:73)
at org.eclipse.jdt.ls.core.internal.handlers.JDTLanguageServer.lambda$7(JDTLanguageServer.java:658)
at org.eclipse.jdt.ls.core.internal.BaseJDTLanguageServer.lambda$0(BaseJDTLanguageServer.java:87)
at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:646)
... 6 more
")
The above setting for lsp-java does work, so I wonder why passing the same jvm argument will cause the discrepancy.
Default value of
eglot-java-eclipse-jdt-ls-download-url
is a snapshot download. If you are unlucky to install it a time when the latest snapshot is buggy, good luck understanding what's wrong when you are not familiar with eglot.
Switching from release snapshots to release milestones could mitigate this issue.
Sadly, it seems that there's no official Eclipse website API endpoint to retrieve available jdtls
releases. (i.e., in comparison to let's say fetching GitHub releases for a given project).
One option is to leverage the Homebrew jdtls formula REST endpoint to retrieve the latest "known" stable version.
The property paths of interest are .urls.stable.url
and .versions.stable
, per JSON response exceprt below.
{
"name": "jdtls",
...
"versions": {
"stable": "1.30.0",
"head": null,
"bottle": true
},
"urls": {
"stable": {
"url": "https://www.eclipse.org/downloads/download.php?file=/jdtls/milestones/1.30.0/jdt-language-server-1.30.0-202311301503.tar.gz",
"tag": null,
"revision": null,
"using": null,
"checksum": "579809f27df5f4e53566217f7273fa25cd45a22d3870c0aafd4f84cdd4c97acd"
}
},
...
}
We probably need to record the installed jdtls
version ( in some form of metadata file??).
jdtls
version recorded in the Homebrew formula might not be the latest available release milestone.One of the great things about getting java development going on lsp-mode is the ability for lsp-mode to install jdtls for you. would be great to have that added here.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.