blob: 0b11cb6ff4f791344804843dc37b49c0d5f7f703 [file] [log] [blame]
Tor Norbye3a2425a2013-11-04 10:16:08 -08001#@PydevCodeAnalysisIgnore
2'''
Tor Norbyec667c1f2014-05-28 17:06:51 -07003@author Fabio Zadrozny
Tor Norbye3a2425a2013-11-04 10:16:08 -08004'''
5IS_PYTHON3K = 0
6try:
7 import __builtin__
8except ImportError:
Tor Norbyec667c1f2014-05-28 17:06:51 -07009 import builtins as __builtin__ # Python 3.0
Tor Norbye3a2425a2013-11-04 10:16:08 -080010 IS_PYTHON3K = 1
11
12try:
Tor Norbyec667c1f2014-05-28 17:06:51 -070013 True
14 False
15except NameError:
16 # If it's not defined, let's define it now.
17 setattr(__builtin__, 'True', 1) # Python 3.0 does not accept __builtin__.True = 1 in its syntax
Tor Norbye3a2425a2013-11-04 10:16:08 -080018 setattr(__builtin__, 'False', 0)
19
20import pydevd_constants
21
22try:
23 from java.lang import Thread
24 IS_JYTHON = True
25 SERVER_NAME = 'jycompletionserver'
Tor Norbyec667c1f2014-05-28 17:06:51 -070026 import _pydev_jy_imports_tipper # as _pydev_imports_tipper #changed to be backward compatible with 1.5
27 _pydev_imports_tipper = _pydev_jy_imports_tipper
Tor Norbye3a2425a2013-11-04 10:16:08 -080028
29except ImportError:
Tor Norbyec667c1f2014-05-28 17:06:51 -070030 # it is python
Tor Norbye3a2425a2013-11-04 10:16:08 -080031 IS_JYTHON = False
32 SERVER_NAME = 'pycompletionserver'
33 if pydevd_constants.USE_LIB_COPY:
34 from _pydev_threading import Thread
35 else:
36 from threading import Thread
Tor Norbyec667c1f2014-05-28 17:06:51 -070037 import _pydev_imports_tipper
Tor Norbye3a2425a2013-11-04 10:16:08 -080038
39
Tor Norbye1aa2e092014-08-20 17:01:23 -070040from _pydev_imps import _pydev_socket as socket
Tor Norbye3a2425a2013-11-04 10:16:08 -080041
42import sys
43if sys.platform == "darwin":
Tor Norbyec667c1f2014-05-28 17:06:51 -070044 # See: https://sourceforge.net/projects/pydev/forums/forum/293649/topic/3454227
Tor Norbye3a2425a2013-11-04 10:16:08 -080045 try:
Tor Norbyec667c1f2014-05-28 17:06:51 -070046 import _CF # Don't fail if it doesn't work -- do it because it must be loaded on the main thread! @UnresolvedImport @UnusedImport
Tor Norbye3a2425a2013-11-04 10:16:08 -080047 except:
48 pass
49
50
Tor Norbyec667c1f2014-05-28 17:06:51 -070051# initial sys.path
Tor Norbye3a2425a2013-11-04 10:16:08 -080052_sys_path = []
53for p in sys.path:
Tor Norbyec667c1f2014-05-28 17:06:51 -070054 # changed to be compatible with 1.5
Tor Norbye3a2425a2013-11-04 10:16:08 -080055 _sys_path.append(p)
56
Tor Norbyec667c1f2014-05-28 17:06:51 -070057# initial sys.modules
Tor Norbye3a2425a2013-11-04 10:16:08 -080058_sys_modules = {}
59for name, mod in sys.modules.items():
60 _sys_modules[name] = mod
61
62
63import traceback
64
Tor Norbye1aa2e092014-08-20 17:01:23 -070065from _pydev_imps import _pydev_time as time
Tor Norbye3a2425a2013-11-04 10:16:08 -080066
67try:
68 import StringIO
69except:
70 import io as StringIO #Python 3.0
71
72try:
73 from urllib import quote_plus, unquote_plus
74except ImportError:
75 from urllib.parse import quote_plus, unquote_plus #Python 3.0
76
77INFO1 = 1
78INFO2 = 2
79WARN = 4
80ERROR = 8
81
82DEBUG = INFO1 | ERROR
83
84def dbg(s, prior):
85 if prior & DEBUG != 0:
86 sys.stdout.write('%s\n' % (s,))
87# f = open('c:/temp/test.txt', 'a')
88# print_ >> f, s
89# f.close()
Tor Norbye1aa2e092014-08-20 17:01:23 -070090
Tor Norbye3a2425a2013-11-04 10:16:08 -080091import pydev_localhost
92HOST = pydev_localhost.get_localhost() # Symbolic name meaning the local host
93
94MSG_KILL_SERVER = '@@KILL_SERVER_END@@'
95MSG_COMPLETIONS = '@@COMPLETIONS'
96MSG_END = 'END@@'
97MSG_INVALID_REQUEST = '@@INVALID_REQUEST'
98MSG_JYTHON_INVALID_REQUEST = '@@JYTHON_INVALID_REQUEST'
99MSG_CHANGE_DIR = '@@CHANGE_DIR:'
100MSG_OK = '@@MSG_OK_END@@'
Tor Norbye3a2425a2013-11-04 10:16:08 -0800101MSG_IMPORTS = '@@IMPORTS:'
102MSG_PYTHONPATH = '@@PYTHONPATH_END@@'
103MSG_CHANGE_PYTHONPATH = '@@CHANGE_PYTHONPATH:'
Tor Norbyec667c1f2014-05-28 17:06:51 -0700104MSG_JEDI = '@@MSG_JEDI:'
Tor Norbye3a2425a2013-11-04 10:16:08 -0800105MSG_SEARCH = '@@SEARCH'
106
107BUFFER_SIZE = 1024
108
109
110
111currDirModule = None
112
Tor Norbyec667c1f2014-05-28 17:06:51 -0700113def CompleteFromDir(directory):
Tor Norbye3a2425a2013-11-04 10:16:08 -0800114 '''
Tor Norbyec667c1f2014-05-28 17:06:51 -0700115 This is necessary so that we get the imports from the same directory where the file
Tor Norbye3a2425a2013-11-04 10:16:08 -0800116 we are completing is located.
117 '''
118 global currDirModule
119 if currDirModule is not None:
Tor Norbyec667c1f2014-05-28 17:06:51 -0700120 if len(sys.path) > 0 and sys.path[0] == currDirModule:
121 del sys.path[0]
Tor Norbye3a2425a2013-11-04 10:16:08 -0800122
Tor Norbyec667c1f2014-05-28 17:06:51 -0700123 currDirModule = directory
124 sys.path.insert(0, directory)
Tor Norbye3a2425a2013-11-04 10:16:08 -0800125
126
127def ChangePythonPath(pythonpath):
128 '''Changes the pythonpath (clears all the previous pythonpath)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700129
Tor Norbye3a2425a2013-11-04 10:16:08 -0800130 @param pythonpath: string with paths separated by |
131 '''
Tor Norbyec667c1f2014-05-28 17:06:51 -0700132
Tor Norbye3a2425a2013-11-04 10:16:08 -0800133 split = pythonpath.split('|')
134 sys.path = []
135 for path in split:
136 path = path.strip()
137 if len(path) > 0:
138 sys.path.append(path)
Tor Norbye3a2425a2013-11-04 10:16:08 -0800139
Tor Norbyec667c1f2014-05-28 17:06:51 -0700140
Tor Norbye3a2425a2013-11-04 10:16:08 -0800141class Processor:
142
143 def __init__(self):
Tor Norbyec667c1f2014-05-28 17:06:51 -0700144 # nothing to do
145 return
146
Tor Norbye3a2425a2013-11-04 10:16:08 -0800147 def removeInvalidChars(self, msg):
148 try:
149 msg = str(msg)
150 except UnicodeDecodeError:
151 pass
Tor Norbyec667c1f2014-05-28 17:06:51 -0700152
Tor Norbye3a2425a2013-11-04 10:16:08 -0800153 if msg:
154 try:
155 return quote_plus(msg)
156 except:
157 sys.stdout.write('error making quote plus in %s\n' % (msg,))
158 raise
159 return ' '
Tor Norbyec667c1f2014-05-28 17:06:51 -0700160
Tor Norbye3a2425a2013-11-04 10:16:08 -0800161 def formatCompletionMessage(self, defFile, completionsList):
162 '''
163 Format the completions suggestions in the following format:
164 @@COMPLETIONS(modFile(token,description),(token,description),(token,description))END@@
165 '''
166 compMsg = []
167 compMsg.append('%s' % defFile)
168 for tup in completionsList:
169 compMsg.append(',')
Tor Norbyec667c1f2014-05-28 17:06:51 -0700170
Tor Norbye3a2425a2013-11-04 10:16:08 -0800171 compMsg.append('(')
Tor Norbyec667c1f2014-05-28 17:06:51 -0700172 compMsg.append(str(self.removeInvalidChars(tup[0]))) # token
Tor Norbye3a2425a2013-11-04 10:16:08 -0800173 compMsg.append(',')
Tor Norbyec667c1f2014-05-28 17:06:51 -0700174 compMsg.append(self.removeInvalidChars(tup[1])) # description
Tor Norbye3a2425a2013-11-04 10:16:08 -0800175
176 if(len(tup) > 2):
177 compMsg.append(',')
Tor Norbyec667c1f2014-05-28 17:06:51 -0700178 compMsg.append(self.removeInvalidChars(tup[2])) # args - only if function.
179
Tor Norbye3a2425a2013-11-04 10:16:08 -0800180 if(len(tup) > 3):
181 compMsg.append(',')
Tor Norbyec667c1f2014-05-28 17:06:51 -0700182 compMsg.append(self.removeInvalidChars(tup[3])) # TYPE
183
Tor Norbye3a2425a2013-11-04 10:16:08 -0800184 compMsg.append(')')
Tor Norbyec667c1f2014-05-28 17:06:51 -0700185
Tor Norbye3a2425a2013-11-04 10:16:08 -0800186 return '%s(%s)%s' % (MSG_COMPLETIONS, ''.join(compMsg), MSG_END)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700187
Tor Norbye3a2425a2013-11-04 10:16:08 -0800188
189class T(Thread):
190
Tor Norbyec667c1f2014-05-28 17:06:51 -0700191 def __init__(self, port):
Tor Norbye3a2425a2013-11-04 10:16:08 -0800192 Thread.__init__(self)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700193 self.ended = False
194 self.port = port
195 self.socket = None # socket to send messages.
Tor Norbye3a2425a2013-11-04 10:16:08 -0800196 self.processor = Processor()
197
198
199 def connectToServer(self):
Tor Norbye1aa2e092014-08-20 17:01:23 -0700200 from _pydev_imps import _pydev_socket as socket
Tor Norbyec667c1f2014-05-28 17:06:51 -0700201
Tor Norbye3a2425a2013-11-04 10:16:08 -0800202 self.socket = s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
203 try:
Tor Norbyec667c1f2014-05-28 17:06:51 -0700204 s.connect((HOST, self.port))
Tor Norbye3a2425a2013-11-04 10:16:08 -0800205 except:
Tor Norbyec667c1f2014-05-28 17:06:51 -0700206 sys.stderr.write('Error on connectToServer with parameters: host: %s port: %s\n' % (HOST, self.port))
Tor Norbye3a2425a2013-11-04 10:16:08 -0800207 raise
208
209 def getCompletionsMessage(self, defFile, completionsList):
210 '''
211 get message with completions.
212 '''
213 return self.processor.formatCompletionMessage(defFile, completionsList)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700214
Tor Norbye3a2425a2013-11-04 10:16:08 -0800215 def getTokenAndData(self, data):
216 '''
217 When we receive this, we have 'token):data'
218 '''
219 token = ''
220 for c in data:
221 if c != ')':
222 token = token + c
223 else:
224 break;
Tor Norbyec667c1f2014-05-28 17:06:51 -0700225
Tor Norbye3a2425a2013-11-04 10:16:08 -0800226 return token, data.lstrip(token + '):')
227
Tor Norbyec667c1f2014-05-28 17:06:51 -0700228 def emulated_sendall(self, msg):
229 MSGLEN = 1024 * 20
230
231 totalsent = 0
232 while totalsent < MSGLEN:
233 sent = self.socket.send(msg[totalsent:])
234 if sent == 0:
235 return
236 totalsent = totalsent + sent
237
238
239 def send(self, msg):
240 if not hasattr(self.socket, 'sendall'):
241 #Older versions (jython 2.1)
242 self.emulated_sendall(msg)
243 else:
244 if IS_PYTHON3K:
245 self.socket.sendall(bytearray(msg, 'utf-8'))
246 else:
247 self.socket.sendall(msg)
248
249
Tor Norbye3a2425a2013-11-04 10:16:08 -0800250 def run(self):
251 # Echo server program
252 try:
253 import _pydev_log
254 log = _pydev_log.Log()
Tor Norbyec667c1f2014-05-28 17:06:51 -0700255
256 dbg(SERVER_NAME + ' connecting to java server on %s (%s)' % (HOST, self.port) , INFO1)
257 # after being connected, create a socket as a client.
Tor Norbye3a2425a2013-11-04 10:16:08 -0800258 self.connectToServer()
Tor Norbyec667c1f2014-05-28 17:06:51 -0700259
260 dbg(SERVER_NAME + ' Connected to java server', INFO1)
261
262
263 while not self.ended:
Tor Norbye3a2425a2013-11-04 10:16:08 -0800264 data = ''
Tor Norbyec667c1f2014-05-28 17:06:51 -0700265
Tor Norbye3a2425a2013-11-04 10:16:08 -0800266 while data.find(MSG_END) == -1:
Tor Norbyec667c1f2014-05-28 17:06:51 -0700267 received = self.socket.recv(BUFFER_SIZE)
Tor Norbye3a2425a2013-11-04 10:16:08 -0800268 if len(received) == 0:
Tor Norbyec667c1f2014-05-28 17:06:51 -0700269 sys.exit(0) # ok, connection ended
Tor Norbye3a2425a2013-11-04 10:16:08 -0800270 if IS_PYTHON3K:
271 data = data + received.decode('utf-8')
272 else:
273 data = data + received
Tor Norbyec667c1f2014-05-28 17:06:51 -0700274
Tor Norbye3a2425a2013-11-04 10:16:08 -0800275 try:
276 try:
277 if data.find(MSG_KILL_SERVER) != -1:
278 dbg(SERVER_NAME + ' kill message received', INFO1)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700279 # break if we received kill message.
Tor Norbye3a2425a2013-11-04 10:16:08 -0800280 self.ended = True
281 sys.exit(0)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700282
Tor Norbye3a2425a2013-11-04 10:16:08 -0800283 dbg(SERVER_NAME + ' starting keep alive thread', INFO2)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700284
Tor Norbye3a2425a2013-11-04 10:16:08 -0800285 if data.find(MSG_PYTHONPATH) != -1:
286 comps = []
287 for p in _sys_path:
288 comps.append((p, ' '))
Tor Norbyec667c1f2014-05-28 17:06:51 -0700289 self.send(self.getCompletionsMessage(None, comps))
290
Tor Norbye3a2425a2013-11-04 10:16:08 -0800291 else:
292 data = data[:data.rfind(MSG_END)]
Tor Norbyec667c1f2014-05-28 17:06:51 -0700293
Tor Norbye3a2425a2013-11-04 10:16:08 -0800294 if data.startswith(MSG_IMPORTS):
Tor Norbyec667c1f2014-05-28 17:06:51 -0700295 data = data[len(MSG_IMPORTS):]
Tor Norbye3a2425a2013-11-04 10:16:08 -0800296 data = unquote_plus(data)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700297 defFile, comps = _pydev_imports_tipper.GenerateTip(data, log)
298 self.send(self.getCompletionsMessage(defFile, comps))
299
Tor Norbye3a2425a2013-11-04 10:16:08 -0800300 elif data.startswith(MSG_CHANGE_PYTHONPATH):
Tor Norbyec667c1f2014-05-28 17:06:51 -0700301 data = data[len(MSG_CHANGE_PYTHONPATH):]
Tor Norbye3a2425a2013-11-04 10:16:08 -0800302 data = unquote_plus(data)
303 ChangePythonPath(data)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700304 self.send(MSG_OK)
305
306 elif data.startswith(MSG_JEDI):
307 data = data[len(MSG_JEDI):]
Tor Norbye3a2425a2013-11-04 10:16:08 -0800308 data = unquote_plus(data)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700309 line, column, encoding, path, source = data.split('|', 4)
310 try:
311 import jedi # @UnresolvedImport
312 except:
313 self.send(self.getCompletionsMessage(None, [('Error on import jedi', 'Error importing jedi', '')]))
314 else:
315 script = jedi.Script(
316 # Line +1 because it expects lines 1-based (and col 0-based)
317 source=source,
318 line=int(line) + 1,
319 column=int(column),
320 source_encoding=encoding,
321 path=path,
322 )
323 lst = []
324 for completion in script.completions():
325 t = completion.type
326 if t == 'class':
327 t = '1'
328
329 elif t == 'function':
330 t = '2'
331
332 elif t == 'import':
333 t = '0'
334
335 elif t == 'keyword':
336 continue # Keywords are already handled in PyDev
337
338 elif t == 'statement':
339 t = '3'
340
341 else:
342 t = '-1'
343
344 # gen list(tuple(name, doc, args, type))
345 lst.append((completion.name, '', '', t))
346 self.send(self.getCompletionsMessage('empty', lst))
347
348 elif data.startswith(MSG_SEARCH):
349 data = data[len(MSG_SEARCH):]
350 data = unquote_plus(data)
351 (f, line, col), foundAs = _pydev_imports_tipper.Search(data)
352 self.send(self.getCompletionsMessage(f, [(line, col, foundAs)]))
353
Tor Norbye3a2425a2013-11-04 10:16:08 -0800354 elif data.startswith(MSG_CHANGE_DIR):
Tor Norbyec667c1f2014-05-28 17:06:51 -0700355 data = data[len(MSG_CHANGE_DIR):]
Tor Norbye3a2425a2013-11-04 10:16:08 -0800356 data = unquote_plus(data)
357 CompleteFromDir(data)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700358 self.send(MSG_OK)
359
Tor Norbye3a2425a2013-11-04 10:16:08 -0800360 else:
Tor Norbyec667c1f2014-05-28 17:06:51 -0700361 self.send(MSG_INVALID_REQUEST)
Tor Norbye3a2425a2013-11-04 10:16:08 -0800362 except SystemExit:
Tor Norbyec667c1f2014-05-28 17:06:51 -0700363 self.send(self.getCompletionsMessage(None, [('Exit:', 'SystemExit', '')]))
Tor Norbye3a2425a2013-11-04 10:16:08 -0800364 raise
Tor Norbyec667c1f2014-05-28 17:06:51 -0700365
Tor Norbye3a2425a2013-11-04 10:16:08 -0800366 except:
367 dbg(SERVER_NAME + ' exception occurred', ERROR)
368 s = StringIO.StringIO()
369 traceback.print_exc(file=s)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700370
Tor Norbye3a2425a2013-11-04 10:16:08 -0800371 err = s.getvalue()
372 dbg(SERVER_NAME + ' received error: ' + str(err), ERROR)
Tor Norbyec667c1f2014-05-28 17:06:51 -0700373 self.send(self.getCompletionsMessage(None, [('ERROR:', '%s\nLog:%s' % (err, log.GetContents()), '')]))
374
375
Tor Norbye3a2425a2013-11-04 10:16:08 -0800376 finally:
377 log.Clear()
Tor Norbyec667c1f2014-05-28 17:06:51 -0700378
379 self.socket.close()
Tor Norbye3a2425a2013-11-04 10:16:08 -0800380 self.ended = True
Tor Norbyec667c1f2014-05-28 17:06:51 -0700381 sys.exit(0) # connection broken
382
383
Tor Norbye3a2425a2013-11-04 10:16:08 -0800384 except SystemExit:
385 raise
Tor Norbyec667c1f2014-05-28 17:06:51 -0700386 # No need to log SystemExit error
Tor Norbye3a2425a2013-11-04 10:16:08 -0800387 except:
388 s = StringIO.StringIO()
389 exc_info = sys.exc_info()
390
391 traceback.print_exception(exc_info[0], exc_info[1], exc_info[2], limit=None, file=s)
392 err = s.getvalue()
393 dbg(SERVER_NAME + ' received error: ' + str(err), ERROR)
394 raise
395
Tor Norbyec667c1f2014-05-28 17:06:51 -0700396
397
Tor Norbye3a2425a2013-11-04 10:16:08 -0800398if __name__ == '__main__':
399
Tor Norbyec667c1f2014-05-28 17:06:51 -0700400 port = int(sys.argv[1]) # this is from where we want to receive messages.
401
402 t = T(port)
Tor Norbye3a2425a2013-11-04 10:16:08 -0800403 dbg(SERVER_NAME + ' will start', INFO1)
404 t.start()
405 time.sleep(5)
406 t.join()