Access Dart Analysis Server from Java


In my last few blogs (here, here and here) I started implementing a smart dart editor with syntax highlighting, auto complete, … for google dart.

While the first posts have been dedicated to getting syntax highlighting working we’ll now start with the higher level features you are used to from full blown IDEs.

In this first blog post we’ll look how we can interface with the Dart Analysis Server which is the reason I’ve chose dart as the example language.

The communication with the Dart Analysis is done through the input and output stream. An example will probably help.

Let’s suppose we have a /home/tomschindl/dart-samples/test.dart with the following content:

class Rectangle {
  num left;   
  num top;
  num width; 
  num height;          
  
  num get right             => left + width;
      set right(num value)  => left = value - width;
  num get bottom            => top + height;
      set bottom(num value) => top = value - height;
}

// This is where the app starts executing.
main() {
  var r = new Rectangle();
  r.
}

If you now want to get all auto completetion proposals after r. you issue the following commands/requests to the server:

  • You launch the dart analysis server eg on my workstation it is:
    /Users/tomschindl/Downloads/dart-sdk/bin/dart \
     bin/snapshots/analysis_server.dart.snapshot
  • You inform the server about the root directory:
    { 
      "id" : "default_1", 
      "method" : "analysis.setAnalysisRoots" , 
      "params" :  {
         "included":["/Users/tomschindl/dart-samples/"],
         "excluded":[]
      }
    }
    
  • Which will synchronously return the following result

    {
      "id":"default_1"
    }
    
  • Requesting all auto completetions at offset 367 which is directly after r.

    { 
      "id" : "default_2", 
      "method" : "completion.getSuggestions" , 
      "params" :  {
        "file":"/Users/tomschindl/dart-samples/test.dart",
        "offset":367
      }
    }
    
  • Which will synchronously return the following result
    {
      "id":"default_2",
      "result":{
        "id":"0"
      }
    }
    
  • And asynchronously the following events will occur

    {
      "event":"completion.results",
      "params":{
        "id":"0",
        "replacementOffset":367,
        "replacementLength":0,
        "results":[],
        "isLast":false
      }
    }
    
    {
      "event":"completion.results",
       "params":{
         "id":"0",
         "replacementOffset":367,
         "replacementLength":0,
         "results": [
           {
             "kind":"INVOCATION",
             "relevance":1000,
             "completion":"left",
             "selectionOffset":4,
             "selectionLength":0,
             "isDeprecated":false,
             "isPotential":false,
             "declaringType":"Rectangle",
             "element":{
               "kind":"FIELD",
               "name":"left",
               "location":{
                 "file":"/Users/tomschindl/dart-samples/test.dart",
                 "offset":24,
                 "length":4,
                 "startLine":2,
                 "startColumn":7
               },
               "flags":0,
               "returnType":"num"
             },"returnType":"num"
           },
           {
             "kind":"INVOCATION",
             "relevance":1000,
             "completion":"right",
             "selectionOffset":5,
             "selectionLength":0,
             "isDeprecated":false,
             "isPotential":false,
             "declaringType":"Rectangle",
             "element":{
               "kind":"GETTER",
               "name":"right",
               "location":{
                 "file":"/Users/tomschindl/dart-samples/test.dart",
                 "offset":95,
                 "length":5,
                 "startLine":7,
                 "startColumn":11
               },
               "flags":0,
               "returnType":"num"
             },
             "returnType":"num"
           }
           // Many more
         ],
        "isLast":true
      }
    }
    

I guess you get the idea – not really rocket science but in java we don’t want to deal with this low-level stuff. So as part of the editor work we also developed a Java interface for the Dart Analysis Server available from maven-central.

If we want to issue the same commands as above through the Java-API.

Create a maven-project and make the pom.xml look like this:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>at.bestsolution</groupId>
	<artifactId>at.bestsolution.dart.server.sample</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<repositories>
		<repository>
			<id>sonatype-snapshots</id>
			<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
		</repository>
	</repositories>
	<dependencies>
		<dependency>
			<groupId>at.bestsolution.eclipse</groupId>
			<artifactId>org.eclipse.fx.core</artifactId>
			<version>2.1.0-SNAPSHOT</version>
		</dependency>
		<dependency>
			<groupId>at.bestsolution</groupId>
			<artifactId>at.bestsolution.dart.server.api</artifactId>
			<version>1.0.0</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

And a java class with the following content:

package at.bestsolution.dart.server.sample;

import java.io.IOException;
import java.util.stream.Stream;

import org.eclipse.fx.core.Util;

import at.bestsolution.dart.server.api.DartServer;
import at.bestsolution.dart.server.api.DartServerFactory;
import at.bestsolution.dart.server.api.Registration;
import at.bestsolution.dart.server.api.model.CompletionResultsNotification;
import at.bestsolution.dart.server.api.services.ServiceAnalysis;
import at.bestsolution.dart.server.api.services.ServiceCompletion;

public class DartServerSample {
	public static void main(String[] args) {
		// Get the server factory from the service registry
		DartServerFactory serverFactory = Util.lookupService(DartServerFactory.class);
		// Create a server instance
		DartServer server = serverFactory.getServer("server");

		// Get the analysis and completion service
		ServiceAnalysis analysisService = server.getService(ServiceAnalysis.class);
		ServiceCompletion completionService = server.getService(ServiceCompletion.class);

		// set the root
		analysisService.setAnalysisRoots(new String[] {"/Users/tomschindl/dart-samples/"}, new String[0], null);
		// register for completion notifcations
		Registration proposalRegistration = completionService.results(DartServerSample::handleHandleResults);

		// Request completion at offset 367
		completionService.getSuggestions("/Users/tomschindl/dart-samples/test.dart", 367);

		// Wait for a key press
		try {
			System.in.read();
		} catch (IOException e) {
		}

		// unregister the notification listener
		proposalRegistration.dispose();
		// shutdown the server instance
		server.dispose();
	}

	private static void handleHandleResults(CompletionResultsNotification notification) {
		Stream.of(notification.getResults()).forEach( c -> System.err.println(c.getCompletion()));
	}
}

And then you have to launch the class with

java -Ddart.sdkdir=/Users/tomschindl/Downloads/dart-sdk at.bestsolution.dart.server.sample.DartServerSample

where you replace /Users/tomschindl/Downloads/dart-sdk with the path to your dart-sdk installation.

This entry was posted in e(fx)clipse. Bookmark the permalink.

1 Response to Access Dart Analysis Server from Java

  1. Pingback: e(fx)clipse 2.1.0 released | Tomsondev Blog

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.