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

EclipseCon Europe 2018: Extending JDT with Lang...

EclipseCon Europe 2018: Extending JDT with Language Servers

Avatar for Martin Lippert

Martin Lippert

October 25, 2018
Tweet

More Decks by Martin Lippert

Other Decks in Programming

Transcript

  1. 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
  2. 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
  3. 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
  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/ Part 1 - Understanding Java inside the LS
  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/ 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
  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/ 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
  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/ 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
  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 9 but where does the classpath information come from?
  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/ 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
  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/ Part 2 - Running the Language Server
  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/ 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
  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 - 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
  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 - 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
  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/ 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
  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/ Part 3 - Connecting to the Java 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/ 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
  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/ 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
  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); } ... } 19 regular content-assist processor from LSP4E
  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); } ... } 20 call the LSP4E content- assist processor, that’s it
  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/ Live Demo
  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/ 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
  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/ 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
  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); } ... } 24 regular hover provider from LSP4E
  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); } ... } 25 call the LSP4E hover provider, that’s it
  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/ Live Demo
  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/ Side Notes • to improve reliability, wrap calls to LS… into futures 27
  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/ Code Lenses you need to do NOTHING… 28
  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/ Live Demo
  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/ Outlook
  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 • 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