This page is no longer maintained — Please continue to the home page at www.scala-lang.org

jEdit ENSIME Thingy

5 replies
daniel
Joined: 2008-08-20,
User offline. Last seen 44 weeks 14 hours ago.
https://github.com/djspiewak/ensime-sidekick

It occurred to me that perhaps this might be useful to someone else, even in its current state.  It's basically the beginnings of a port of the Emacs ENSIME functionality over to jEdit.  There is an existing plugin which attempts to do this in jEdit, but it embeds a modified version of ENSIME (and doesn't work post 2.8.1), so its usefulness is a bit diminished.

Right now, the following features work:
  • Typecheck on save (with error underlines; warnings not supported yet)
  • Inspect type under cursor (no special support for arrow types yet, so they look a little odd)
  • Jump to declaration (my favorite!)
I'll keep adding to this as I need features for my own use.  If anyone else wants to jump in and add stuff, feel free!  It's worth noting that there is a prototype of popup completion implemented using the SideKick completion framework.  Unfortunately, this is very non-trivial functionality that I don't really need, so it probably won't get done any time soon.  (the reason it is non-trivial is ENSIME requires that jEdit knows the structural context of the caret in order to request the right sort of completion; jEdit doesn't have that knowledge, so my code basically needs to do its own reverse parsing back from the caret to try to guess its context)

The big problem right now is the implementation is very machine specific (and specifically my machine).  Again, this is largely because I need it for my own stuff and haven't gotten around to making it work for anyone else.  The machine specific bits are documented in the README.  Help here is also appreciated, especially if you're an SBT or a scala.sys.process guru.  Or we can just leave it specific to my machine; works for me!  :-)

Oh, another issue is that the plugin only maintains a single instance of ENSIME.  That should be fixed soon, but until then, you will need to re-init ENSIME any time you switch to a file that's in a different project.  Make sure you have a pre-existing .ensime file consisting of a single s-expression (no comments)!

No promises on maintenance here.  As I said, I did this in anger since I really need this functionality for my own work.  Any functions that I don't need (such as scaladoc lookup, debugging or file structure view) are unlikely to get implemented or maintained (in the event of someone else contributing an implementation).  I'll maintain this exactly as long as I continue to use it myself.  :-)

With all that said, enjoy!

Daniel
Aemon Cannon
Joined: 2010-03-21,
User offline. Last seen 1 year 24 weeks ago.
Re: jEdit ENSIME Thingy
Awesome! I'm glad you were able to get it going without modifying the server.Anyone who might end up hacking on this and has questions about ENSIME stuff, please hit me up! 


On Tue, Aug 23, 2011 at 11:01 AM, Daniel Spiewak <djspiewak [at] gmail [dot] com> wrote:
https://github.com/djspiewak/ensime-sidekick

It occurred to me that perhaps this might be useful to someone else, even in its current state.  It's basically the beginnings of a port of the Emacs ENSIME functionality over to jEdit.  There is an existing plugin which attempts to do this in jEdit, but it embeds a modified version of ENSIME (and doesn't work post 2.8.1), so its usefulness is a bit diminished.

Right now, the following features work:
  • Typecheck on save (with error underlines; warnings not supported yet)
  • Inspect type under cursor (no special support for arrow types yet, so they look a little odd)
  • Jump to declaration (my favorite!)
I'll keep adding to this as I need features for my own use.  If anyone else wants to jump in and add stuff, feel free!  It's worth noting that there is a prototype of popup completion implemented using the SideKick completion framework.  Unfortunately, this is very non-trivial functionality that I don't really need, so it probably won't get done any time soon.  (the reason it is non-trivial is ENSIME requires that jEdit knows the structural context of the caret in order to request the right sort of completion; jEdit doesn't have that knowledge, so my code basically needs to do its own reverse parsing back from the caret to try to guess its context)

The big problem right now is the implementation is very machine specific (and specifically my machine).  Again, this is largely because I need it for my own stuff and haven't gotten around to making it work for anyone else.  The machine specific bits are documented in the README.  Help here is also appreciated, especially if you're an SBT or a scala.sys.process guru.  Or we can just leave it specific to my machine; works for me!  :-)

Oh, another issue is that the plugin only maintains a single instance of ENSIME.  That should be fixed soon, but until then, you will need to re-init ENSIME any time you switch to a file that's in a different project.  Make sure you have a pre-existing .ensime file consisting of a single s-expression (no comments)!

No promises on maintenance here.  As I said, I did this in anger since I really need this functionality for my own work.  Any functions that I don't need (such as scaladoc lookup, debugging or file structure view) are unlikely to get implemented or maintained (in the event of someone else contributing an implementation).  I'll maintain this exactly as long as I continue to use it myself.  :-)

With all that said, enjoy!

Daniel

daniel
Joined: 2008-08-20,
User offline. Last seen 44 weeks 14 hours ago.
Re: jEdit ENSIME Thingy
Actually, I have two questions.  One is for me, and one is for the poor sots who have been trying to do exactly this sort of thing.

First, could you link to some SWANK protocol documentation from the ENSIME website?  While telnet-heavy reverse engineering and source-code-reading is a really fun way to spend an afternoon, it's probably not the fastest way to implement tooling.  All I really needed was the documentation on how exactly the sockets are used in SWANK and specifically how to send/receive an s-expression.  Figuring that out actually required more than twice as much time as it took me to do the full implementation for "jump to declaration"!

Second, could you explain how exactly ENSIME server processes are meant to be juggled?  This is one I haven't figured out yet.  I have a vague notion that it's supposed to be one process per project (or subproject, as the case may be).  How do you associate those processes (inside Emacs) with files that haven't been opened yet?  I know how I would do it, but I'd rather mimic Emacs's semantics here.

Daniel

On Tue, Aug 23, 2011 at 11:43 AM, Aemon Cannon <aemoncannon [at] gmail [dot] com> wrote:
Awesome! I'm glad you were able to get it going without modifying the server.Anyone who might end up hacking on this and has questions about ENSIME stuff, please hit me up! 


On Tue, Aug 23, 2011 at 11:01 AM, Daniel Spiewak <djspiewak [at] gmail [dot] com> wrote:
https://github.com/djspiewak/ensime-sidekick

It occurred to me that perhaps this might be useful to someone else, even in its current state.  It's basically the beginnings of a port of the Emacs ENSIME functionality over to jEdit.  There is an existing plugin which attempts to do this in jEdit, but it embeds a modified version of ENSIME (and doesn't work post 2.8.1), so its usefulness is a bit diminished.

Right now, the following features work:
  • Typecheck on save (with error underlines; warnings not supported yet)
  • Inspect type under cursor (no special support for arrow types yet, so they look a little odd)
  • Jump to declaration (my favorite!)
I'll keep adding to this as I need features for my own use.  If anyone else wants to jump in and add stuff, feel free!  It's worth noting that there is a prototype of popup completion implemented using the SideKick completion framework.  Unfortunately, this is very non-trivial functionality that I don't really need, so it probably won't get done any time soon.  (the reason it is non-trivial is ENSIME requires that jEdit knows the structural context of the caret in order to request the right sort of completion; jEdit doesn't have that knowledge, so my code basically needs to do its own reverse parsing back from the caret to try to guess its context)

The big problem right now is the implementation is very machine specific (and specifically my machine).  Again, this is largely because I need it for my own stuff and haven't gotten around to making it work for anyone else.  The machine specific bits are documented in the README.  Help here is also appreciated, especially if you're an SBT or a scala.sys.process guru.  Or we can just leave it specific to my machine; works for me!  :-)

Oh, another issue is that the plugin only maintains a single instance of ENSIME.  That should be fixed soon, but until then, you will need to re-init ENSIME any time you switch to a file that's in a different project.  Make sure you have a pre-existing .ensime file consisting of a single s-expression (no comments)!

No promises on maintenance here.  As I said, I did this in anger since I really need this functionality for my own work.  Any functions that I don't need (such as scaladoc lookup, debugging or file structure view) are unlikely to get implemented or maintained (in the event of someone else contributing an implementation).  I'll maintain this exactly as long as I continue to use it myself.  :-)

With all that said, enjoy!

Daniel


Aemon Cannon
Joined: 2010-03-21,
User offline. Last seen 1 year 24 weeks ago.
Re: jEdit ENSIME Thingy
First:
The best that exists right now is at: http://aemon.com/file_dump/ensime_manual.html#tth_sEcC.2
It leaves a lot to be desired. I'll see what I can do to improve things.

Second:
As you say, it's one process per project. After the server is initialized it sends some project 
information up to the client, including the source roots. Like so:

(:return
 (:ok
  (:project-name "ensime" :source-roots
		 ("/home/aemon/src/misc/ensime/src/main/scala" 
		 "/home/aemon/src/misc/ensime/src/main/java" 
		 "/home/aemon/src/misc/ensime/src/test/scala"))) 2)

This information is stored with the connection on which it was received. When a new file is opened, 
the list of connections is searched for a connection(project) that has a source root that contains the 
file.
It gets a bit more complicated for projects that have external source dependencies (imagine if you
have two sbt subprojects open that both need to be notified if sources in some third subproject are
changed), but that's the basic idea.

On Tue, Aug 23, 2011 at 12:51 PM, Daniel Spiewak <djspiewak [at] gmail [dot] com> wrote:
Actually, I have two questions.  One is for me, and one is for the poor sots who have been trying to do exactly this sort of thing.

First, could you link to some SWANK protocol documentation from the ENSIME website?  While telnet-heavy reverse engineering and source-code-reading is a really fun way to spend an afternoon, it's probably not the fastest way to implement tooling.  All I really needed was the documentation on how exactly the sockets are used in SWANK and specifically how to send/receive an s-expression.  Figuring that out actually required more than twice as much time as it took me to do the full implementation for "jump to declaration"!

Second, could you explain how exactly ENSIME server processes are meant to be juggled?  This is one I haven't figured out yet.  I have a vague notion that it's supposed to be one process per project (or subproject, as the case may be).  How do you associate those processes (inside Emacs) with files that haven't been opened yet?  I know how I would do it, but I'd rather mimic Emacs's semantics here.

Daniel

On Tue, Aug 23, 2011 at 11:43 AM, Aemon Cannon <aemoncannon [at] gmail [dot] com> wrote:
Awesome! I'm glad you were able to get it going without modifying the server.Anyone who might end up hacking on this and has questions about ENSIME stuff, please hit me up! 


On Tue, Aug 23, 2011 at 11:01 AM, Daniel Spiewak <djspiewak [at] gmail [dot] com> wrote:
https://github.com/djspiewak/ensime-sidekick

It occurred to me that perhaps this might be useful to someone else, even in its current state.  It's basically the beginnings of a port of the Emacs ENSIME functionality over to jEdit.  There is an existing plugin which attempts to do this in jEdit, but it embeds a modified version of ENSIME (and doesn't work post 2.8.1), so its usefulness is a bit diminished.

Right now, the following features work:
  • Typecheck on save (with error underlines; warnings not supported yet)
  • Inspect type under cursor (no special support for arrow types yet, so they look a little odd)
  • Jump to declaration (my favorite!)
I'll keep adding to this as I need features for my own use.  If anyone else wants to jump in and add stuff, feel free!  It's worth noting that there is a prototype of popup completion implemented using the SideKick completion framework.  Unfortunately, this is very non-trivial functionality that I don't really need, so it probably won't get done any time soon.  (the reason it is non-trivial is ENSIME requires that jEdit knows the structural context of the caret in order to request the right sort of completion; jEdit doesn't have that knowledge, so my code basically needs to do its own reverse parsing back from the caret to try to guess its context)

The big problem right now is the implementation is very machine specific (and specifically my machine).  Again, this is largely because I need it for my own stuff and haven't gotten around to making it work for anyone else.  The machine specific bits are documented in the README.  Help here is also appreciated, especially if you're an SBT or a scala.sys.process guru.  Or we can just leave it specific to my machine; works for me!  :-)

Oh, another issue is that the plugin only maintains a single instance of ENSIME.  That should be fixed soon, but until then, you will need to re-init ENSIME any time you switch to a file that's in a different project.  Make sure you have a pre-existing .ensime file consisting of a single s-expression (no comments)!

No promises on maintenance here.  As I said, I did this in anger since I really need this functionality for my own work.  Any functions that I don't need (such as scaladoc lookup, debugging or file structure view) are unlikely to get implemented or maintained (in the event of someone else contributing an implementation).  I'll maintain this exactly as long as I continue to use it myself.  :-)

With all that said, enjoy!

Daniel



daniel
Joined: 2008-08-20,
User offline. Last seen 44 weeks 14 hours ago.
Re: jEdit ENSIME Thingy
First:
The best that exists right now is at: http://aemon.com/file_dump/ensime_manual.html#tth_sEcC.2
It leaves a lot to be desired. I'll see what I can do to improve things.
Really the only thing that's needed is the documentation of the raw s-expression protocol.  The API calls can be determined by reading SwankProtocol.scala.  Specifically, I'm looking for something like this:

To send an s-expression, determine its ASCII length and then encode that integer as a padded six-digit hexadecimal value.  Write this value to the output socket first, then followed by the ASCII form of the s-expression.  Thus, the reader loop should read six ASCII characters into a buffer and convert that into an integer, then read that number of ASCII characters from the socket, parsing the result into an s-expression.

Basically, that's anyone would need.  Some raw, telnet-able examples would be nifty too, but not necessary.  The rest is API.
As you say, it's one process per project. After the server is initialized it sends some project 
information up to the client, including the source roots. Like so:

(:return
 (:ok
  (:project-name "ensime" :source-roots
		 ("/home/aemon/src/misc/ensime/src/main/scala" 
		 "/home/aemon/src/misc/ensime/src/main/java" 
		 "/home/aemon/src/misc/ensime/src/test/scala"))) 2)

This information is stored with the connection on which it was received. When a new file is opened, 
the list of connections is searched for a connection(project) that has a source root that contains the 
file.
Ok, that makes sense.  Thanks!

Daniel
Aemon Cannon
Joined: 2010-03-21,
User offline. Last seen 1 year 24 weeks ago.
Re: jEdit ENSIME Thingy
FYI Updated the SWANK doc a bit, http://aemon.com/file_dump/ensime_manual.html#tth_sEcC.2 .. using your explanation almost verbatim.


On Tue, Aug 23, 2011 at 1:45 PM, Daniel Spiewak <djspiewak [at] gmail [dot] com> wrote:
First:
The best that exists right now is at: http://aemon.com/file_dump/ensime_manual.html#tth_sEcC.2
It leaves a lot to be desired. I'll see what I can do to improve things.
Really the only thing that's needed is the documentation of the raw s-expression protocol.  The API calls can be determined by reading SwankProtocol.scala.  Specifically, I'm looking for something like this:

To send an s-expression, determine its ASCII length and then encode that integer as a padded six-digit hexadecimal value.  Write this value to the output socket first, then followed by the ASCII form of the s-expression.  Thus, the reader loop should read six ASCII characters into a buffer and convert that into an integer, then read that number of ASCII characters from the socket, parsing the result into an s-expression.

Basically, that's anyone would need.  Some raw, telnet-able examples would be nifty too, but not necessary.  The rest is API.
As you say, it's one process per project. After the server is initialized it sends some project 
information up to the client, including the source roots. Like so:

(:return
 (:ok
  (:project-name "ensime" :source-roots
		 ("/home/aemon/src/misc/ensime/src/main/scala" 
		 "/home/aemon/src/misc/ensime/src/main/java" 
		 "/home/aemon/src/misc/ensime/src/test/scala"))) 2)

This information is stored with the connection on which it was received. When a new file is opened, 
the list of connections is searched for a connection(project) that has a source root that contains the 
file.
Ok, that makes sense.  Thanks!

Daniel

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland