c9-core/plugins/c9.ide.language.codeintel/server/codeintel_server.py

206 wiersze
7.3 KiB
Python
Executable File

import argparse
import json
import sys
import os
import logging
from os import path
import urlparse
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
# Hackfix codeintel import
sys.path.extend([p + "/codeintel" for p in sys.path])
# Hackfix inflector compatibility
import inflector
class MyInflector: Inflector = inflector.Inflector
class MyInflector2: Inflector = MyInflector
sys.modules["inflector.Inflector"] = MyInflector2
sys.modules["inflector"] = MyInflector2
import inflector.Inflector
from codeintel2.common import EvalController
from codeintel2.manager import Manager
from codeintel2.environment import DefaultEnvironment
def main(args):
if args.mode != "daemon":
print(process(sys.stdin.read(), args.__dict__))
manager.finalize()
return
try:
server = HTTPServer(("localhost", int(args.port)), Daemon)
except:
sys.stderr.write("Daemon unable to listen at :%s\n" % args.port)
sys.exit(98)
sys.stderr.write("!!Daemon " + "listening at :%s\n" % args.port) # concat strings to avoid matching this string in the client
server.serve_forever()
def process(source, args):
mode = args["mode"]
buffer, line, offset = process_input(source, args)
scan_workspace(buffer)
if mode == "completions":
return json.dumps(get_completions(buffer, line, offset))
if mode == "definitions":
return json.dumps(get_definitions(buffer, line, offset))
if mode == "calltips":
return json.dumps(get_calltips(buffer, line, offset))
if mode == "catalogs":
return json.dumps(get_catalogs(buffer, line, offset))
def get_completions(buffer, line, offset):
trigger = buffer.preceding_trg_from_pos(offset, offset)
if trigger is None:
return []
results = buffer.cplns_from_trg(trigger, ctlr = LoggingEvalController(), timeout = 120)
if results is None:
return []
return [
remove_nulls({
"name": get_proposal_name(kind, name, buffer.lang, trigger),
"replaceText": get_proposal_replace_text(kind, name, buffer.lang, trigger, line),
"icon": {
"function": "method",
"module": "package",
"class": "package",
"element": "event",
"property": "property",
"value": "method",
}.get(kind, "property"),
}) for kind, name in results
]
def get_definitions(buffer, line, offset):
trigger = buffer.defn_trg_from_pos(offset)
if trigger is None:
return []
results = buffer.defns_from_trg(trigger, ctlr = LoggingEvalController(), timeout = 10)
if results is None:
return []
return {
"path": "/" + results[0].path,
"row": results[0].line - 1,
}
def get_calltips(buffer, line, offset):
trigger = buffer.preceding_trg_from_pos(offset, offset)
if trigger is None:
return []
result = buffer.calltips_from_trg(trigger, ctlr = LoggingEvalController(), timeout = 10)
if result is None:
return ""
return result
def get_catalogs(buffer, line, offset):
return [c for c in manager.db.get_catalogs_zone().avail_catalogs()]
def get_proposal_name(kind, name, lang, trigger):
if lang == "PHP" and kind == "variable" and trigger.name != "php-complete-object-members":
return "$" + name
if kind == "function" or kind == "method":
return name + "()"
return name
def get_proposal_replace_text(kind, name, lang, trigger, line):
if lang == "PHP" and kind == "variable" and trigger.name != "php-complete-object-members":
return "$" + name
if "import " in line:
return name
if kind == "function" or kind == "method":
return name + "(^^)"
def remove_nulls(d):
for key, value in d.items():
if value is None:
del d[key]
elif isinstance(value, dict):
remove_nulls(value)
return d
def process_input(source, args):
row = int(args.get("row"))
column = int(args.get("column"))
path = args.get("path")
basedir = args.get("basedir")
language = args.get("language")
dirs = args.get("dirs")
catalogs = args.get("catalogs").split(",") if args.get("catalogs") else []
env = manager.env = DefaultEnvironment()
env.set_pref("codeintel_selected_catalogs", catalogs)
env.set_pref("extra_module_dirs", dirs)
env.set_pref("phpExtraPaths", dirs)
env.set_pref("codeintel_scan_extra_dir", dirs)
env.set_pref("codeintel_scan_files_in_project", True)
env.get_proj_base_dir = lambda: basedir
buffer = manager.buf_from_content(source + "\n", language, path = path, env = env)
lines = source.split('\n')
line = lines[row]
offset = sum([len(l) + 1 for l in lines[:row]]) + column
return buffer, line, offset
def scan_workspace(buffer):
if not manager.is_citadel_lang(buffer.lang):
return
if not buffer.lang in scanned_langs:
sys.stderr.write("!!Updating " + buffer.lang + " indexes\n")
scanned_langs.append(buffer.lang)
buffer.scan()
sys.stderr.write("!!Done updating indexes\n")
manager.db.save()
else:
buffer.scan()
class LoggingEvalController(EvalController):
def debug(self, msg, *args): logger.debug(msg, *args)
def info(self, msg, *args): logger.info(msg, *args)
def warn(self, msg, *args): logger.warn(msg, *args)
def error(self, msg, *args): logger.error(msg, *args)
class Daemon(BaseHTTPRequestHandler):
def do_POST(self):
query = urlparse.urlparse(self.path).query
args = urlparse.parse_qsl(query)
length = int(self.headers.getheader("content-length", 0))
source = self.rfile.read(length)
self.send_response(200)
self.end_headers()
self.wfile.write(process(source, dict(args)))
def log_message(self, format, *args):
return # log silently
logStream = logging.StreamHandler(sys.stderr)
logStream.setFormatter(logging.Formatter("%(name)s: %(levelname)s: %(message)s"))
logging.getLogger("codeintel").addHandler(logStream)
logger = logging.getLogger("codeintel_server")
logger.addHandler(logStream)
logger.setLevel(logging.WARNING)
manager = Manager(
# db_base_dir = path.join(CI_DIR, 'db'),
# extra_module_dirs = [path.join(CI_DIR, 'codeintel2'),],
# db_import_everything_langs = None,
# db_catalog_dirs = []
)
manager.upgrade()
manager.initialize()
scanned_langs = []
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Run codeintel commands as a daemon or via stdin")
parser.add_argument("mode", help="Mode of operation", choices=["daemon", "completions", "definitions", "calltips", "catalogs"])
parser.add_argument("--row", type=int, help="The row to read from")
parser.add_argument("--column", type=int, help="The column to read from")
parser.add_argument("--path", help="The path of the file")
parser.add_argument("--basedir", help="The basedir of the file")
parser.add_argument("--language", help="The language of the file")
parser.add_argument("--port", type=int, help="The port for the daemon to listen on")
parser.add_argument("--nodoc", help="Don't include docstrings in output")
parser.add_argument("--catalogs", help="Catalogs to include (comma-separated)")
parser.add_argument("--dirs", help="Directories to include (comma-separated)")
args = parser.parse_args()
main(args)