Upgrade to Pro — share decks privately, control downloads, hide ads and more …

EclipseCon Europe 2018: Extending JDT with Language Servers

EclipseCon Europe 2018: Extending JDT with Language Servers

D65e990ab4df0d0a75c3b0ae5d036bc5?s=128

Martin Lippert

October 25, 2018
Tweet

Transcript

  1. Extending JDT with Language Servers Martin Lippert - Pivotal @martinlippert

  2. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ The Background
  3. Unless otherwise indicated, these slides are © 2013-2017 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Spring Tools 4 - now GA 3 https://spring.io/tools Spring Tools 4 for Eclipse Spring Tools 4 for Visual Studio Code Spring Tools 4 for Atom IDE
  4. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Integration with JDT is key
  5. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Part 1 - Understanding Java inside the LS
  6. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Understanding Java • Language Servers are different from Eclipse extensions • they know about files • there is no API to understand and analyze languages • You have to build your own language understanding for Java • heavyweight approach: re-use full-blown Eclipse JDT on OSGi • lightweight approach: use a Java language parser of your choice and go 6
  7. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Parsing Java private void scanFiles(IJavaProject project, ASTParser parser, String[] javaFiles, String[] classpathEntries) throws Exception { Map<String, String> options = JavaCore.getOptions(); JavaCore.setComplianceOptions(JavaCore.VERSION_10, options); ASTParser parser = ASTParser.newParser(AST.JLS10); parser.setCompilerOptions(options); parser.setKind(ASTParser.K_COMPILATION_UNIT); parser.setStatementsRecovery(true); parser.setBindingsRecovery(true); parser.setResolveBindings(true); parser.setIgnoreMethodBodies(false); String[] sourceEntries = new String[0]; parser.setEnvironment(classpathEntries, sourceEntries, null, false); FileASTRequestor requestor = new FileASTRequestor() { @Override public void acceptAST(String sourceFilePath, CompilationUnit cu) { ... } }; parser.createASTs(javaFiles, null, new String[0], requestor, null); } 7
  8. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ private void scanFiles(IJavaProject project, ASTParser parser, String[] javaFiles, String[] classpathEntries) throws Exception { Map<String, String> options = JavaCore.getOptions(); JavaCore.setComplianceOptions(JavaCore.VERSION_10, options); ASTParser parser = ASTParser.newParser(AST.JLS10); parser.setCompilerOptions(options); parser.setKind(ASTParser.K_COMPILATION_UNIT); parser.setStatementsRecovery(true); parser.setBindingsRecovery(true); parser.setResolveBindings(true); parser.setIgnoreMethodBodies(false); String[] sourceEntries = new String[0]; parser.setEnvironment(classpathEntries, sourceEntries, null, false); FileASTRequestor requestor = new FileASTRequestor() { @Override public void acceptAST(String sourceFilePath, CompilationUnit cu) { ... } }; parser.createASTs(javaFiles, null, new String[0], requestor, null); } Parsing Java 8 depends on your code analysis requirements, skipping method bodies improves performance
  9. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ private void scanFiles(IJavaProject project, ASTParser parser, String[] javaFiles, String[] classpathEntries) throws Exception { Map<String, String> options = JavaCore.getOptions(); JavaCore.setComplianceOptions(JavaCore.VERSION_10, options); ASTParser parser = ASTParser.newParser(AST.JLS10); parser.setCompilerOptions(options); parser.setKind(ASTParser.K_COMPILATION_UNIT); parser.setStatementsRecovery(true); parser.setBindingsRecovery(true); parser.setResolveBindings(true); parser.setIgnoreMethodBodies(false); String[] sourceEntries = new String[0]; parser.setEnvironment(classpathEntries, sourceEntries, null, false); FileASTRequestor requestor = new FileASTRequestor() { @Override public void acceptAST(String sourceFilePath, CompilationUnit cu) { ... } }; parser.createASTs(javaFiles, null, new String[0], requestor, null); } Parsing Java 9 but where does the classpath information come from?
  10. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Resolving Projects • first attempt: • understand the project structure on our own (use Gradle, Maven, etc.) • => slow, error-prone, could result in mismatches with Java language tooling • second attempt: • ask the Java tooling (Java language server) for resolved projects and updates • requires extra work on the Eclipse side (needs to deliver that information) • requires extra communication 10
  11. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Part 2 - Running the Language Server
  12. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Language Server <extension point="org.eclipse.lsp4e.languageServer"> <server class="org.springframework.tooling.boot.ls.DelegatingStreamConnectionProvider" id="org.eclipse.languageserver.languages.springboot" clientImpl="org.springframework.tooling.ls.eclipse.commons.STS4LanguageClientImpl" label="Spring Boot Language Server"> </server> <contentTypeMapping contentType="org.eclipse.jdt.core.javaSource" id="org.eclipse.languageserver.languages.springboot"> </contentTypeMapping> </extension> 12
  13. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Language Server - Definition <extension point="org.eclipse.lsp4e.languageServer"> <server class="org.springframework.tooling.boot.ls.DelegatingStreamConnectionProvider" id="org.eclipse.languageserver.languages.springboot" clientImpl="org.springframework.tooling.ls.eclipse.commons.STS4LanguageClientImpl" label="Spring Boot Language Server"> </server> <contentTypeMapping contentType="org.eclipse.jdt.core.javaSource" id="org.eclipse.languageserver.languages.springboot"> </contentTypeMapping> </extension> 13 define the language server via the LSP4E extension point
  14. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Language Server - Mapping <extension point="org.eclipse.lsp4e.languageServer"> <server class="org.springframework.tooling.boot.ls.DelegatingStreamConnectionProvider" id="org.eclipse.languageserver.languages.springboot" clientImpl="org.springframework.tooling.ls.eclipse.commons.STS4LanguageClientImpl" label="Spring Boot Language Server"> </server> <contentTypeMapping contentType="org.eclipse.jdt.core.javaSource" id="org.eclipse.languageserver.languages.springboot"> </contentTypeMapping> </extension> 14 map the language server to the existing Java source content type
  15. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Connect Document Events <extension point="org.eclipse.core.filebuffers.documentSetup"> <participant class="org.eclipse.lsp4e.ConnectDocumentToLanguageServerSetupParticipant" contentTypeId="org.eclipse.jdt.core.javaSource"> </participant> </extension> 15 the document connector is responsible for starting and stopping language servers automatically, independent of the used editor
  16. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Part 3 - Connecting to the Java Editor
  17. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Connecting Content-Assist <extension id="springbootjava-completion-computer" point="org.eclipse.jdt.ui.javaCompletionProposalComputer"> <javaCompletionProposalComputer activate="true" categoryId="org.eclipse.jdt.ui.defaultProposalCategory" class="org.springframework.tooling.boot.ls.jdt.SpringBootJavaCompletionProposalComputer" needsSortingAfterFiltering="false"> </javaCompletionProposalComputer> </extension> 17 write and define a regular content-assist proposal computer for JDT
  18. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Completion Proposal Computer public class SpringBootJavaCompletionProposalComputer implements IJavaCompletionProposalComputer { private LSContentAssistProcessor lsContentAssistProcessor; public SpringBootJavaCompletionProposalComputer() { lsContentAssistProcessor = new LSContentAssistProcessor(); } @Override public List<ICompletionProposal> computeCompletionProposals(ContentAssistInvocationContext context, IProgressMonitor monitor) { ICompletionProposal[] completionProposals = lsContentAssistProcessor.computeCompletionProposals( context.getViewer(), context.getInvocationOffset()); return Arrays.asList(completionProposals); } @Override public List<IContextInformation> computeContextInformation(ContentAssistInvocationContext context, IProgressMonitor monitor) { IContextInformation[] contextInformation = lsContentAssistProcessor.computeContextInformation( context.getViewer(), context.getInvocationOffset()); return Arrays.asList(contextInformation); } ... } 18
  19. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Completion Proposal Computer public class SpringBootJavaCompletionProposalComputer implements IJavaCompletionProposalComputer { private LSContentAssistProcessor lsContentAssistProcessor; public SpringBootJavaCompletionProposalComputer() { lsContentAssistProcessor = new LSContentAssistProcessor(); } @Override public List<ICompletionProposal> computeCompletionProposals(ContentAssistInvocationContext context, IProgressMonitor monitor) { ICompletionProposal[] completionProposals = lsContentAssistProcessor.computeCompletionProposals( context.getViewer(), context.getInvocationOffset()); return Arrays.asList(completionProposals); } @Override public List<IContextInformation> computeContextInformation(ContentAssistInvocationContext context, IProgressMonitor monitor) { IContextInformation[] contextInformation = lsContentAssistProcessor.computeContextInformation( context.getViewer(), context.getInvocationOffset()); return Arrays.asList(contextInformation); } ... } 19 regular content-assist processor from LSP4E
  20. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Completion Proposal Computer public class SpringBootJavaCompletionProposalComputer implements IJavaCompletionProposalComputer { private LSContentAssistProcessor lsContentAssistProcessor; public SpringBootJavaCompletionProposalComputer() { lsContentAssistProcessor = new LSContentAssistProcessor(); } @Override public List<ICompletionProposal> computeCompletionProposals(ContentAssistInvocationContext context, IProgressMonitor monitor) { ICompletionProposal[] completionProposals = lsContentAssistProcessor.computeCompletionProposals( context.getViewer(), context.getInvocationOffset()); return Arrays.asList(completionProposals); } @Override public List<IContextInformation> computeContextInformation(ContentAssistInvocationContext context, IProgressMonitor monitor) { IContextInformation[] contextInformation = lsContentAssistProcessor.computeContextInformation( context.getViewer(), context.getInvocationOffset()); return Arrays.asList(contextInformation); } ... } 20 call the LSP4E content- assist processor, that’s it
  21. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Live Demo
  22. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Connecting Live Hovers <extension id="springbootjava-hover-provider" point="org.eclipse.jdt.ui.javaEditorTextHovers"> <hover activate="true" class="org.springframework.tooling.boot.ls.jdt.SpringBootJavaHoverProvider" id="org.springframework.boot.ide.java.servers.hoverprovider"> </hover> </extension> 22 write and define a regular hover provider for JDT
  23. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Hover Provider public class SpringBootJavaHoverProvider implements IJavaEditorTextHover, ITextHoverExtension { private LSBasedHover lsBasedHover; public SpringBootJavaHoverProvider() { lsBasedHover = new LSBasedHover(); } @Override public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) { return this.lsBasedHover.getHoverInfo(textViewer, hoverRegion); } @Override public IRegion getHoverRegion(ITextViewer textViewer, int offset) { return this.lsBasedHover.getHoverRegion(textViewer, offset); } ... } 23
  24. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Hover Provider public class SpringBootJavaHoverProvider implements IJavaEditorTextHover, ITextHoverExtension { private LSBasedHover lsBasedHover; public SpringBootJavaHoverProvider() { lsBasedHover = new LSBasedHover(); } @Override public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) { return this.lsBasedHover.getHoverInfo(textViewer, hoverRegion); } @Override public IRegion getHoverRegion(ITextViewer textViewer, int offset) { return this.lsBasedHover.getHoverRegion(textViewer, offset); } ... } 24 regular hover provider from LSP4E
  25. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Hover Provider public class SpringBootJavaHoverProvider implements IJavaEditorTextHover, ITextHoverExtension { private LSBasedHover lsBasedHover; public SpringBootJavaHoverProvider() { lsBasedHover = new LSBasedHover(); } @Override public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) { return this.lsBasedHover.getHoverInfo(textViewer, hoverRegion); } @Override public IRegion getHoverRegion(ITextViewer textViewer, int offset) { return this.lsBasedHover.getHoverRegion(textViewer, offset); } ... } 25 call the LSP4E hover provider, that’s it
  26. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Live Demo
  27. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Side Notes • to improve reliability, wrap calls to LS… into futures 27
  28. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Code Lenses you need to do NOTHING… 28
  29. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Live Demo
  30. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Outlook
  31. Unless otherwise indicated, these slides are © 2013-2018 Pivotal Software,

    Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Outlook • get push model in LSP for: • code lenses (custom messages at the moment) • green highlights in the code (custom messages at the moment) • investigate more complicated code analysis tasks • e.g. what happens if we implement more comprehensive content-assist 31
  32. > Thanks. https://spring.io/tools https://github.com/spring-projects/sts4 Martin Lippert mlippert@pivotal.io