Hello all--
I'm Paul Berry. I'm one of the engineers working on the Dart Editor (the flagship IDE for Dart development) and the Dart Analysis Server (the mechanism that we hope to make available soon to support rich analysis and refactoring services in a large number of editors, including the Dart Editor itself).
There's been a recent surge of interest in writing editor plugins that use the analysis server. I'm currently aware of people in the community interested in writing plugins for vim, DevStudio, and sublime. (I'm also planning to do a plugin for emacs). This is really exciting, since the more popular editors we can support, the more users will be able to jump straight into Dart development without having to learn how to use an entirely new development environment. So I thought I'd write up a brief overview of what the analysis server is and how to use it. We're in the process of putting together a larger (and much more complete) specification document, but it's not ready yet, so hopefully this email can hold you over while you wait.
Getting and running the analysis server
The analysis server depends on a number of other packages. You can either get these packages by running "pub get" from the analysis_server directory, or, if you have a full local build of the dart SDK, you can point your package root at its "packages" directory. So, for example, this is how I'm currently running the analysis server on my mac:
paulberry-macbookpro:dart paulberry$ sdk/bin/dart --package-root=xcodebuild/ReleaseIA32/packages pkg/analysis_server/bin/server.dart
In the long run our plan is to build the analysis server into the released SDK, so that users won't have to mess around with "pub get" or have a full local build of the SDK in order to use plugins that take advantage of it.
Communicating with the analysis server
The analysis server expects to communicate with its client using standard input and output. Each line that is received on standard input is interpreted as a JSON request, and each line that it sends on standard output is either a JSON response to a specific request or a JSON notification. (Note that unlike a typical use of JSON, line breaks are not permitted within a single JSON request--each request must be contained on a single line).
The detailed structure of the JSON requests, responses, and notifications hasn't quite been settled on yet, but I'll walk through an example to give you an idea of what to expect, and then I'll follow up with some pointers to where to get more information. Feel free to ask questions on this list!
Example: Getting errors, warnings, and hints
In the example below, the words "SEND:" and "RECV:" are not part of the protocol--they are just to indicate which messages are sent to the server and which messages are received from the server.
RECV: {"event":"server.connected"}
This is always the first message from the server. The client can watch for this to determine that the server started up successfully and is ready to analyze files.
SEND: {"id":"0","method":"analysis.setAnalysisRoots","params":{"included":["/var/folders/55/83dl8yk507bbspsg0b1vd5b0007dgy/T/analysisServerw7MAbh"],"excluded":[]}}
This request tells the server where to find the source code to analyze. (The crazy pathname "/var/folders/55/83dl8yk507bbspsg0b1vd5b0007dgy/T/analysisServerw7MAbh" is because I captured this trace from an integration test, which puts all its files in a temporary directory). The server will analyze all files that are descendants of that directory, as well as files in any dependent packages. Note that the pathname is in a list--this allows the client to request that multiple directories should be analyzed. Note also that the "id" string can be anything--it's just used to associate requests with responses.
This is a response from the server acknowledging that the above request completed successfully. Successful completion of the "analysis.setAnalysisRoots" request doesn't mean that everything has been analyzed--it just means that the server has updated its notion of where to go looking for files to analyze.
If the request had been malformed, the response would have contained an "error" field describing the problem.
(Note: some requests ask for data from the analysis server. The responses to these requests will contain a "data" field containing the requested data).
RECV: {"params":{"analysis":{"analyzing":true}},"event":"server.status"}
This is a notification from the server telling the client that analysis has begun. Clients might find it useful to put up a subtle visual indication that analysis is in progress so that the user knows what's happening.
RECV: {"params":{"errors":[],"file":"/var/folders/55/83dl8yk507bbspsg0b1vd5b0007dgy/T/analysisServerw7MAbh/test.dart"},"event":"analysis.errors"}
This is the first of several notifications telling the client what errors, warnings, and hints have been found for the file "test.dart". The analysis server operates in several stages, and it reports back errors, warnings, and hints after each stage, so that the user can see results as soon as they're available. This notification has an empty list for "errors", meaning that no errors have been detected in "test.dart" yet.
RECV: {"params":{"errors":[{"severity":"ERROR","type":"SYNTACTIC_ERROR","location":{"file":"/var/folders/55/83dl8yk507bbspsg0b1vd5b0007dgy/T/analysisServerw7MAbh/test.dart","offset":15,"length":1,"startLine":2,"startColumn":7},"message":"Expected to find ';'"}],"file":"/var/folders/55/83dl8yk507bbspsg0b1vd5b0007dgy/T/analysisServerw7MAbh/test.dart"},"event":"analysis.errors"}
Now more analysis has been done, and the analysis server has discovered a parse error. Note that the parse error is associated with an offset (measured in unicode characters from the start of the file), a length (again measured in unicode characters), a line and column number (for the convenience of clients that keep track of file locations that way), and a human-readable error message. Each error also has a severity and a type, so that the client can easily do things like display errors more prominently than warnings.
RECV: {"params":{"errors":[{"severity":"ERROR","type":"SYNTACTIC_ERROR","location":{"file":"/var/folders/55/83dl8yk507bbspsg0b1vd5b0007dgy/T/analysisServerw7MAbh/test.dart","offset":15,"length":1,"startLine":2,"startColumn":7},"message":"Expected to find ';'"}],"file":"/var/folders/55/83dl8yk507bbspsg0b1vd5b0007dgy/T/analysisServerw7MAbh/test.dart"},"event":"analysis.errors"}
Now even more analysis has been done, but there are no new errors. Note that after each stage of analysis the server sends a complete list of the errors for the file that was analyzed. So when the client receives this message, it can just throw out the previous list of errors for that file and start using the new list. It doesn't need to accumulate them.
RECV: {"params":{"errors":[{"severity":"ERROR","type":"SYNTACTIC_ERROR","location":{"file":"/var/folders/55/83dl8yk507bbspsg0b1vd5b0007dgy/T/analysisServerw7MAbh/test.dart","offset":15,"length":1,"startLine":2,"startColumn":7},"message":"Expected to find ';'"}],"file":"/var/folders/55/83dl8yk507bbspsg0b1vd5b0007dgy/T/analysisServerw7MAbh/test.dart"},"event":"analysis.errors"}
RECV: {"params":{"errors":[{"severity":"ERROR","type":"SYNTACTIC_ERROR","location":{"file":"/var/folders/55/83dl8yk507bbspsg0b1vd5b0007dgy/T/analysisServerw7MAbh/test.dart","offset":15,"length":1,"startLine":2,"startColumn":7},"message":"Expected to find ';'"}],"file":"/var/folders/55/83dl8yk507bbspsg0b1vd5b0007dgy/T/analysisServerw7MAbh/test.dart"},"event":"analysis.errors"}
Analysis proceeds further. In this particular example no more errors are detected.
RECV: {"params":{"analysis":{"analyzing":false}},"event":"server.status"}
This is a notification from the server that analysis is now complete. At this point the server goes silent.
However, it's not completely idle. The server watches the directory for changes, so if the contents of test.dart are changed, it will automatically be re-analyzed without the client needing to request anything. If one of the files test.dart depends on is changed, and that change causes new errors/warnings/hints in test.dart (or causes errors/warnings/hints in test.dart to go away), then test.dart will automatically be re-analyzed.
Also, if a new file appears, it will automatically get analyzed (provided that it's a descendant of the directory that was specified in analysis.setAnalysisRoots). If a file disappears from the filesystem, it will stop being analyzed, and some notification will be sent to the client (but we haven't precisely decided what that notification will look like yet).
How to find out what's implemented now
I generated the above example by going to the file pkg/analysis_server/test/integration/analysis_error_inttest.dart, adding the line "debugStdio();" to the test_detect_simple_error() method, and running the test. You can do this to any of the integration tests in pkg/analysis_server/test/integration to see what messages are exchanged between client and server. Until we are ready to publish the analysis server spec, this is probably one of your best sources of information about how to use the analysis server. New tests are being added all the time, so check back frequently.
You can also look at the implementation of the analysis server (in particular the files pkg/analysis_server/lib/src/domain_*.dart should give you an idea of what other requests are currently supported). Note that these files refer to constants defined in pkg/analysis_server/lib/src/constants.dart.
What's planned to be implemented
We are planning to entirely replace the analysis engine used by the Dart Editor with the analysis server. So nearly all the features in the Dart Editor should eventually be available, in one way or another, through the analysis server. This includes things like: computing errors/warnings/hints, automated refactoring, "quick fixes", computing the outline for a file, computing a class hierarchy, computing pop-up information about an identifier, jumping from an identifier to its definition, and searching for all usages of a function, method, or class. In addition, the analysis server will be able to deal with files that are modified in the editor but haven't yet been saved to disk. Most of the code to support these features has already been written (after all, these features currently work in the dart editor)--we just need to wire up the protocol so that they can be invoked from the analysis server.
Caveats
The protocol is still in flux, which means that the example in this email, and anything you learn from reading the code, is subject to change. If you spot something that seems wrong, or you have a suggestion or a question about how something works, please feel free to ask!
Additional random information
Paul Jolly asked me some questions in an email before he joined this mailing list. For the benefit of others, I'd like to answer them here:
Hopefully the above explanation contains the information you're looking for. Our assumption is that the user doesn't need a large number of clients, and we didn't want to have to worry about the security implications of opening up a socket that multiple clients could connect to (e.g. on a multi-user machine this could allow one user to snoop another's private files). So we are assuming that if the user launches multiple editor instances, each one will start up and talk to its own server.
We are planning a request 'analysis.updateSdks' that will allow the client to specify the location of the SDK. It isn't implemented yet.
I don't know precisely, but in order to be reasonably usable I imagine it's going to need to be substantially less than a second.
I can't find anything in the spec about this either. But the VM assumes all the input files are valid UTF-8 (and will complain if they are not). I suspect dart2js does the same.
The analysis server assumes UTF-8 both for the files it analyzes and for the communication with the client. And it measures all file offsets and lengths in units of unicode code points, not bytes.
So, short answer: everything is UTF-8.
Yes. The way this will work is that the client will specify the contents of the unsaved file using the "analysis.updateContent" request, and thereafter the analysis server will ignore what's in the filesystem in favor of the data from the client. The client can later specify (again using "analysis.updateContent") that the file is no longer in the unsaved state, and the analysis server will go back to reading the contents of the file from the filesystem.
I hope we can make this work for Vim. If it's a problem, please let us know and we can brainstorm another technique. However I'm hoping it doesn't come to that; I'd rather not support multiple ways of doing things unless we have to.
Hope this is helpful to get folks started!
Paul