Skip to content

Commit

Permalink
Use alternate sexp module that goes to and from sexp. Fix a few ENSIM…
Browse files Browse the repository at this point in the history
…E api incompatibilites.
  • Loading branch information
aemoncannon committed Jan 31, 2012
1 parent 49bb3bb commit db7f3e6
Show file tree
Hide file tree
Showing 4 changed files with 300 additions and 61 deletions.
2 changes: 1 addition & 1 deletion Ensime.sublime-settings
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"ensime_server_path": "Packages/Ensime/server",
"ensime_server_path": "/Users/aemon/projects/ensime/dist",
"ensime_work_dir": "Packages/Ensime/work"
}
112 changes: 70 additions & 42 deletions ensime_client.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import os, sys, stat, time, datetime, re
import functools, socket, threading
import sublime_plugin, sublime
import sexp
from string import strip

from sexp import key,sym


class EnsimeMessageHandler:
Expand All @@ -29,7 +30,6 @@ def port(self):
def receive_loop(self):
#TODO: possibly use a smaller buffer but allow for recomposing a message
# from multiple buffers in case they overflow.
from sexpr_parser import parse
while self.connected:
try:
res = self.client.recv(4096)
Expand All @@ -39,7 +39,8 @@ def receive_loop(self):
msg = res[6:msglen]
nxt = strip(res[msglen:])
while len(nxt) > 0 or len(msg) > 0:
sublime.set_timeout(functools.partial(self.handler.on_data, parse(msg)), 0)
form = sexp.read(msg)
sublime.set_timeout(functools.partial(self.handler.on_data, form), 0)
if len(nxt) > 0:
msglen = int(nxt[:6], 16) + 6
msg = nxt[6:msglen]
Expand Down Expand Up @@ -109,11 +110,10 @@ def __init__(self, settings, window, project_root):
def ignore(d):
None

def clear_notes(lang, data):
if data[-1] == "t":
self.window.active_view().run_command(
"ensime_notes",
{"lang": lang, "action": "clear"})
def clear_notes(lang):
self.window.active_view().run_command(
"ensime_notes",
{"lang": lang, "action": "clear"})

def add_note(lang, data):
self.window.active_view().run_command(
Expand All @@ -122,10 +122,9 @@ def add_note(lang, data):
)

def render_notes(data):
if data[-1] == "t":
self.window.active_view().run_command(
"ensime_notes",
{ "action": "render" }
self.window.active_view().run_command(
"ensime_notes",
{ "action": "render" }
)

self.settings = settings
Expand All @@ -146,14 +145,15 @@ def render_notes(data):
":error": lambda d: sublime.error_message(d[-1])
}
self._server_message_handlers = {
"clear-all-scala-notes": lambda d: clear_notes("scala", d),
"clear-all-java-notes": lambda d: clear_notes("java", d),
"scala-notes": lambda d: add_note("scala", d),
"java-notes": lambda d: add_note("java", d),
"compiler-ready": lambda d: self.window.run_command("random_words_of_encouragement"),
"full-typecheck-finished": render_notes,
"indexer-ready": ignore,
"background-message": sublime.status_message
":clear-all-scala-notes": lambda d: clear_notes("scala"),
":clear-all-java-notes": lambda d: clear_notes("java"),
":scala-notes": lambda d: add_note("scala", d),
":java-notes": lambda d: add_note("java", d),
":compiler-ready":
lambda d: self.window.run_command("random_words_of_encouragement"),
":full-typecheck-finished": render_notes,
":indexer-ready": ignore,
":background-message": sublime.status_message
}

def ready(self):
Expand All @@ -179,28 +179,33 @@ def remove_handler(self, handler_id):
del self.message_handlers[handler_id]

def on_data(self, data):
print "on_data: " + str(data)
self.feedback(str(data))
# match a message with a registered response handler.
# if the message has no registered handler check if it's a
# background message.
if data[0] == ":return":
if data[0] == key(":return"):
th = self._reply_handlers

# if data[0][0][0][1:] == "procedure-id" and self.procedure_handlers.has_key(data[0][0][1]):
# self.procedure_handlers[data[0][0][1]](data)
# del self.proceure_handlers[data[0][0][1]]

if self.message_handlers.has_key(data[-1]):
th[data[1][0]](data)
reply_type = str(data[1][0])
th[reply_type](data)
else:
print "Unhandled message: " + str(data)
else:
self.handle_server_message(data)

def handle_server_message(self, data):
print "handle_server_message: " + str(data)
handled = self._server_message_handlers
try:
if handled.has_key(data[0][1:]):
handled[data[0][1:]](data[-1])
key = str(data[0])
if handled.has_key(key):
handled[key](data[-1])
else:
print "Received a message from the server:"
print str(data)
Expand Down Expand Up @@ -243,19 +248,28 @@ def project_file(self):
if self.ready:
return self.project_root + "/.ensime"
else:
return ""
return None

def project_config(self):
return open(self.project_file()).read()
try:
src = open(self.project_file()).read() if self.project_file() else "()"
conf = sexp.read(src)
conf = conf + [key(":root-dir"), self.project_root]
conf = conf + [key(":active-subproject"), "ensime"]
return conf
except StandardError:
return []


def prepend_length(self, data):
return "%06x" % len(data) + data

def format(self, data, count = None):
if count == None:
return "(:swank-rpc " + str(data) + ")"
if count:
return [key(":swank-rpc"), data, count]
else:
return str("(:swank-rpc " + str(data) + " " + str(count) + ")")
return [key(":swank-rpc"), data]


def req(self, to_send, on_complete = None, msg_id = None):
msgcnt = msg_id
Expand All @@ -265,16 +279,21 @@ def req(self, to_send, on_complete = None, msg_id = None):
if self.ready() and not self.client.connected:
self.client.connect()

msg = ""
msg = None
if on_complete != None:
self.message_handlers[msgcnt] = on_complete
msg = self.format(to_send, msgcnt)
else:
msg = self.format(to_send)

if msg != "":
self.feedback(msg)
self.client.send(self.prepend_length(msg))
msg_str = sexp.to_string(msg)

print "SEND: " + msg_str

self.feedback(msg_str)
self.client.send(self.prepend_length(msg_str))



def disconnect(self):
self._counterLock.acquire()
Expand All @@ -286,27 +305,36 @@ def disconnect(self):
self.client.close()

def handshake(self, on_complete):
self.req("(swank:connection-info)", on_complete)
self.req([sym("swank:connection-info")], on_complete)

def initialize_project(self, on_complete):
self.req("(swank:init-project " + self.project_config() + " )", on_complete)
self.req([sym("swank:init-project"), self.project_config()], on_complete)

def format_source(self, file_path, on_complete):
self.req('(swank:format-source ("'+file_path+'"))', on_complete)
self.req([sym("swank:format-source"),[file_path]], on_complete)

def type_check_all(self, on_complete):
self.req('(swank:typecheck-all)', on_complete)
self.req([sym("swank:typecheck-all")], on_complete)

def type_check_file(self, file_path, on_complete):
self.req('(swank:typecheck-file "' + file_path + '")', on_complete)
self.req([sym("swank:typecheck-file"), file_path], on_complete)

def organize_imports(self, file_path, on_complete):
self.req('(swank:perform-refactor ' + str(self.next_procedure_id()) + ' organizeImports ' +
'(file "'+file_path+'") t)', on_complete)
self.req([sym("swank:perform-refactor"),
self.next_procedure_id(),
sym(organizeImports),
[sym("file"),file_path],
True], on_complete)

def perform_organize(self, previous_id, msg_id, on_complete):
self.req("(swank:exec-refactor " + str(int(previous_id)) + " organizeImports)", on_complete, int(msg_id))
self.req([sym("swank:exec-refactor"),
int(previous_id),
sym("organizeImports")],
on_complete, int(msg_id))

def inspect_type_at_point(self, file_path, position, on_complete):
self.req('(swank:type-at-point "' + file_path + '" ' + str(int(position)) + ")", on_complete)
self.req([sym("swank:type-at-point"),
file_path,
int(position)],
on_complete)

47 changes: 29 additions & 18 deletions ensime_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
import thread
import logging
import subprocess

import sexp
from sexp import key,sym

class ProcessListener(object):
def on_data(self, proc, data):
Expand Down Expand Up @@ -84,7 +85,6 @@ def is_enabled(self):
def _is_scala(self, file_name):
_, fname = os.path.split(file_name)
return fname.lower().endswith(".scala")
# return True

class EnsimeOnly:
def ensime_project_file(self):
Expand All @@ -97,7 +97,8 @@ def ensime_project_file(self):
def is_enabled(self, kill = False):
return bool(ensime_environment.ensime_env.client()) and ensime_environment.ensime_env.client.ready() and bool(self.ensime_project_file())

class EnsimeServerCommand(sublime_plugin.WindowCommand, ProcessListener, ScalaOnly, EnsimeOnly):
class EnsimeServerCommand(sublime_plugin.WindowCommand,
ProcessListener, ScalaOnly, EnsimeOnly):

def ensime_project_root(self):
prj_dirs = [f for f in self.window.folders() if os.path.exists(f + "/.ensime")]
Expand All @@ -110,21 +111,24 @@ def is_started(self):
return hasattr(self, 'proc') and self.proc and self.proc.poll()

def is_enabled(self, **kwargs):
start, kill, show_output = kwargs.get("start", False), kwargs.get("kill", False), kwargs.get("show_output", False)
return ((kill or show_output) and self.is_started()) or (start and bool(self.ensime_project_file()))
start, kill, show_output = (kwargs.get("start", False),
kwargs.get("kill", False),
kwargs.get("show_output", False))
return (((kill or show_output) and self.is_started()) or
(start and bool(self.ensime_project_file())))

def show_output_window(self, show_output = False):
if show_output:
self.window.run_command("show_panel", {"panel": "output.ensime_server"})


def run(self, encoding = "utf-8", env = {}, start = False, quiet = False, kill = False, show_output = False):
print "running: " + self.__class__.__name__
self.show_output = False

def run(self, encoding = "utf-8", env = {},
start = False, quiet = True, kill = False,
show_output = True):
print "Running: " + self.__class__.__name__
self.show_output = show_output
if not hasattr(self, 'settings'):
self.settings = sublime.load_settings("Ensime.sublime-settings")

server_dir = self.settings.get("ensime_server_path")

if kill:
Expand Down Expand Up @@ -166,12 +170,19 @@ def run(self, encoding = "utf-8", env = {}, start = False, quiet = False, kill =
try:
self.show_output = show_output
if start:
cl = EnsimeClient(ensime_environment.ensime_env.settings, self.window, self.ensime_project_root())
sublime.set_timeout(functools.partial(ensime_environment.ensime_env.set_client, cl), 0)
cl = EnsimeClient(
ensime_environment.ensime_env.settings,
self.window, self.ensime_project_root())
sublime.set_timeout(
functools.partial(ensime_environment.ensime_env.set_client, cl), 0)
vw = self.window.active_view()
self.proc = AsyncProcess(['bin/server', self.ensime_project_root() + "/.ensime_port"], self, server_dir)
self.proc = AsyncProcess([
'bin/server',
self.ensime_project_root() + "/.ensime_port"
], self, server_dir)
except err_type as e:
self.append_data(None, str(e) + '\n')
print str(e)
self.append_data(None, str(e) + '\n')

def perform_handshake(self):
self.window.run_command("ensime_handshake")
Expand Down Expand Up @@ -254,12 +265,12 @@ def handle_init_reply(self, init_info):
sublime.status_message("Ensime ready!")

def handle_reply(self, server_info):
if server_info[1][0] == ":ok" and server_info[2] == 1:
msg = "Initializing " + server_info[1][1][3][1] + " v." + server_info[1][1][9]
sublime.status_message(msg)
if server_info[1][0] == key(":ok"):
sublime.status_message("Initializing... ")
ensime_environment.ensime_env.client().initialize_project(self.handle_init_reply)
else:
sublime.error_message("There was problem initializing ensime, msgno: " + str(server_info[2]) + ".")
sublime.error_message("There was problem initializing ensime, msgno: " +
str(server_info[2]) + ".")

def run(self):
if (ensime_environment.ensime_env.client().ready()):
Expand Down
Loading

0 comments on commit db7f3e6

Please sign in to comment.