Skip to content

Commit b16184e

Browse files
committed
PythonCodeCompletion: reimplement completion using rope. rope needs to be installed for the plugin to work correctly (if missing, C::B will probably crash).
1 parent 96883c5 commit b16184e

3 files changed

Lines changed: 127 additions & 149 deletions

File tree

‎PythonCodeCompletion/PythonCodeCompletion.cpp‎

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ wxString PythonCodeCompletion::GetExtraFile(const wxString &short_name, ConfigMa
6464
if(wxFileName::FileExists(fullname))
6565
return fullname;
6666
fullname=mgr->GetDataFolder(true)+short_name;
67+
if(wxFileName::FileExists(fullname))
6768
return fullname;
6869
return wxEmptyString;
6970
}
@@ -96,12 +97,12 @@ void PythonCodeCompletion::OnAttach()
9697

9798
#ifdef __WXMSW__
9899
wxString script = GetExtraFile(_T("/python/python_completion_server.py"),mgr);
99-
wxString stdlib = GetExtraFile(_T("/python/STDLIB"),mgr);
100+
// wxString stdlib = GetExtraFile(_T("/python/STDLIB"),mgr);
100101
#else
101102
wxString script = GetExtraFile(_T("/python/python_completion_server.py"),mgr);
102-
wxString stdlib = GetExtraFile(_T("/python/STDLIB"),mgr);
103+
// wxString stdlib = GetExtraFile(_T("/python/STDLIB"),mgr);
103104
#endif
104-
if(script==wxEmptyString || stdlib == wxEmptyString)
105+
if(script==wxEmptyString /*|| stdlib == wxEmptyString*/)
105106
{
106107
Manager::Get()->GetLogManager()->Log(_T("PyCC: Missing python scripts. Try reinstalling the plugin."));
107108
return;
@@ -211,8 +212,9 @@ void PythonCodeCompletion::OnAttach()
211212

212213
if(port!=-1)
213214
::wxSleep(2); //need a delay to allow the xmlrpc server time to start
214-
Manager::Get()->GetLogManager()->Log(_("Loading stdlib"));
215+
// Manager::Get()->GetLogManager()->Log(_("Loading stdlib"));
215216

217+
m_libs_loaded=true;
216218
//Load the symbol lib synchronously
217219
// XmlRpc::XmlRpcValue(result);
218220
// if(py_server->Exec(_("load_stdlib"),XmlRpc::XmlRpcValue(stdlib.utf8_str()),result))
@@ -221,18 +223,10 @@ void PythonCodeCompletion::OnAttach()
221223
// m_libs_loaded=false;
222224

223225
//Load the symbol lib asynchronously
224-
py_server->ExecAsync(_("load_stdlib"),XmlRpc::XmlRpcValue(stdlib.utf8_str()),this,ID_STDLIB_LOAD);
225-
m_libs_loaded=false;
226+
// py_server->ExecAsync(_("load_stdlib"),XmlRpc::XmlRpcValue(stdlib.utf8_str()),this,ID_STDLIB_LOAD);
227+
// m_libs_loaded=false;
226228
}
227229

228-
//void PythonCodeCompletion::OnLoadTimer()
229-
//{
230-
// if(!m_libs_loaded)
231-
// {
232-
// }
233-
//}
234-
235-
236230

237231
void PythonCodeCompletion::OnRelease(bool appShutDown)
238232
{
@@ -285,12 +279,15 @@ void PythonCodeCompletion::OnCompletePhrase(XmlRpcResponseEvent &event)
285279
{
286280
if(event.GetState()==XMLRPC_STATE_RESPONSE)
287281
{
282+
Manager::Get()->GetLogManager()->Log(_("PYCC: Completion response"));
288283
XmlRpc::XmlRpcValue val=event.GetResponse();
289284
m_comp_results.Empty();
290285
if(val.getType()==val.TypeArray && val.size()>0)
291286
{
287+
Manager::Get()->GetLogManager()->Log(_("PYCC: Getting comp results"));
292288
for(int i=0;i<val.size();++i)
293289
m_comp_results.Add(wxString(std::string(val[i]).c_str(),wxConvUTF8));
290+
Manager::Get()->GetLogManager()->Log(wxString::Format(_("PYCC: Beginning comp with %i items"),val.size()));
294291
CodeComplete();
295292
}
296293
}
@@ -392,8 +389,8 @@ int PythonCodeCompletion::CodeComplete()
392389

393390
void PythonCodeCompletion::ShowCallTip()
394391
{
395-
if (!IsAttached())
396-
return;
392+
if (!IsAttached() || !m_libs_loaded)
393+
return -1;
397394

398395
if (!Manager::Get()->GetEditorManager())
399396
return;
@@ -508,8 +505,13 @@ void PythonCodeCompletion::EditorEventHook(cbEditor* editor, wxScintillaEvent& e
508505
return;
509506
}
510507
wxString phrase=control->GetTextRange(wordStartPos,pos);
511-
Manager::Get()->GetLogManager()->Log(_T("PYCC: Looking for ")+phrase);
512-
py_server->ExecAsync(_T("complete_phrase"),XmlRpc::XmlRpcValue(phrase.mb_str(wxConvUTF8)),this,ID_COMPLETE_PHRASE);
508+
XmlRpc::XmlRpcValue value;
509+
value.setSize(3);
510+
value[0] = editor->GetFilename().mb_str(wxConvUTF8);
511+
value[1] = control->GetText().mb_str(wxConvUTF8);
512+
value[2] = pos;
513+
Manager::Get()->GetLogManager()->Log(_T("PYCC: Looking for ")+phrase+_T(" in ")+editor->GetFilename());
514+
py_server->ExecAsync(_T("complete_phrase"),value,this,ID_COMPLETE_PHRASE);
513515
event.Skip();
514516
return;
515517
}
@@ -592,7 +594,12 @@ void PythonCodeCompletion::EditorEventHook(cbEditor* editor, wxScintillaEvent& e
592594
symbol.Replace(_T("\t"),wxEmptyString);
593595
m_ActiveSymbol=symbol;
594596
Manager::Get()->GetLogManager()->Log(_T("PYCC: Found calltip symbol ")+symbol);
595-
py_server->ExecAsync(_T("complete_tip"),XmlRpc::XmlRpcValue(symbol.mb_str(wxConvUTF8)),this,ID_CALLTIP);
597+
XmlRpc::XmlRpcValue value;
598+
value.setSize(3);
599+
value[0] = editor->GetFilename().mb_str(wxConvUTF8);
600+
value[1] = control->GetText().mb_str(wxConvUTF8);
601+
value[2] = end_pos;
602+
py_server->ExecAsync(_T("complete_tip"),value,this,ID_CALLTIP);
596603
Manager::Get()->GetLogManager()->Log(_T("PYCC: Started server request"));
597604
}
598605
// allow others to handle this event

‎PythonCodeCompletion/python/python_completion_server.py‎

Lines changed: 69 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,28 @@
33
import os.path
44
import time
55
import types
6-
import stdlib_parser
76
import struct
87
import xmlrpclib
98
from SimpleXMLRPCServer import SimpleXMLRPCServer
109
#from pysmell import idehelper
1110
#from pysmell import tags
1211
from re import split
1312

13+
from rope.contrib import codeassist
14+
import rope.base.project
15+
16+
'''
17+
| | instance | class | function | module | None
18+
| local | + | + | + | + |
19+
| global | + | + | + | + |
20+
| builtin | + | + | + | |
21+
| attribute | + | + | + | + |
22+
| imported | + | + | + | + |
23+
| keyword | | | | | +
24+
| parameter_keyword | | | | | +
25+
'''
26+
27+
'''
1428
type_map = {
1529
str(types.ModuleType) : 1,
1630
str(types.ClassType) : 2,
@@ -24,11 +38,21 @@
2438
str(types.BuiltinMethodType) : 7,
2539
str(types.LambdaType) : 9,
2640
str(types.GeneratorType) : 10 }
41+
'''
2742

28-
def type_suffix(symbols,s):
29-
type_str=symbols[s][0]
30-
if type_str in type_map:
31-
return '?'+str(type_map[type_str])
43+
def type_suffix(o):
44+
t = o.type
45+
s = o.scope
46+
if t=='instance':
47+
return '?'+str(5)
48+
elif t=='class':
49+
return '?'+str(2)
50+
elif t=='function':
51+
return '?'+str(6)
52+
elif t=='module':
53+
return '?'+str(1)
54+
elif s=='keyword':
55+
return ''
3256
return ''
3357

3458
def _uniquify(l):
@@ -74,13 +98,14 @@ def __call__(self,name,*args):
7498
return self.fn_dict[name](*args)
7599

76100

77-
class AsyncServer:
101+
class PyCompletionServer:
78102
def __init__(self,port):
79103
# Create XMLRPC server
80104
self.timeout=0.2
81105
self._quit=False
82106
self.port=port
83-
self.std_lib=None
107+
self.project=None
108+
self.active_path=None
84109
def run(self):
85110
if self.port==-1:
86111
self.server = XmlRpcPipeServer()
@@ -90,46 +115,43 @@ def run(self):
90115
self.server.register_introspection_functions()
91116
#self.server.socket.settimeout(self.timeout) ##timeouts cause probs
92117
self.server.register_function(self.end,'end')
93-
self.server.register_function(self.get_completions,'get_completions')
94-
self.server.register_function(self.tag_gen,'tag_gen')
95-
self.server.register_function(self.load_stdlib,'load_stdlib')
96-
self.server.register_function(self.create_stdlib,'create_stdlib')
97118
self.server.register_function(self.complete_phrase,'complete_phrase')
98119
self.server.register_function(self.complete_tip,'complete_tip')
99120
while not self._quit:
100121
self.server.handle_request()
101-
def load_stdlib(self,path):
102-
# print 'loading lib',path
103-
self.stdlib=stdlib_parser.load(path)
104-
return self.stdlib!=None
105-
def create_stdlib(self,path):
106-
# print 'creating',path
107-
self.stdlib=stdlib_parser.create(path)
108-
return self.stdlib!=None
109-
def complete_context(self,path,source,position):
122+
def notify_active_path(self,path):
123+
if self.active_path==os.path.split(path)[0]:
124+
return True
125+
if not os.path.exists(path):
126+
return False
127+
fdir,fname = os.path.split(path)
128+
self.active_path=fdir
129+
try:
130+
self.project = rope.base.project.Project(fdir)
131+
except:
132+
return False
133+
return True
134+
def complete_phrase(self,path,source,position):
110135
# print 'complete context',path,position
111-
pass
112-
def complete_tip(self,symbol):
136+
if path!=None:
137+
if not self.notify_active_path(path):
138+
return []
139+
if self.project==None:
140+
return []
141+
results = codeassist.code_assist(self.project, source, position)
142+
completions=sorted([s.name+type_suffix(s) for s in results])
143+
return completions
144+
def complete_tip(self,path,source,position):
113145
# print 'complete tip',symbol
114-
context=[s.strip() for s in symbol.split('.')]
115-
symbol=context.pop()
116-
psymbols=self.stdlib
117-
for s in context:
118-
if s not in psymbols:
119-
print 'symbol not found'
120-
return ''
121-
psymbols=psymbols[s][-1]
122-
if symbol not in psymbols:
123-
print 'symbol not found'
124-
return ''
125-
symbol_data=psymbols[symbol]
126-
result=''
127-
if symbol_data[1] is not None:
128-
result=symbol+'('+str(symbol_data[1])+')'
129-
else:
130-
if symbol_data[2] is None or not symbol_data[2].startswith(symbol+'('):
131-
result=symbol+'(...)'
132-
doc=str(symbol_data[2])
146+
if path!=None:
147+
if not self.notify_active_path(path):
148+
return []
149+
if self.project==None:
150+
return []
151+
calltip = codeassist.get_calltip(self.project, source, position)
152+
if calltip==None:
153+
calltip=''
154+
doc = codeassist.get_doc(self.project, source, position)
133155
if doc is not None:
134156
lines=doc.split('\n')
135157
shortened=False
@@ -142,63 +164,10 @@ def complete_tip(self,symbol):
142164
doc=doc.strip(' \t\n')
143165
if shortened:
144166
doc+='...'
145-
result+='\n'+doc
146-
print 'result',result
147-
return result
148-
def complete_phrase(self,phrase):
149-
# print 'complete phrase',phrase
150-
context=[s.strip() for s in phrase.split('.')]
151-
phrase=context.pop()
152-
psymbols=self.stdlib
153-
for s in context:
154-
if s not in psymbols:
155-
print 'x',[]
156-
return []
157-
psymbols=psymbols[s][-1]
158-
if type(psymbols)!=types.DictType:
159-
return []
160-
completions=[]
161-
try:
162-
for s in psymbols:
163-
if s.startswith(phrase):
164-
completions.append(s)
165-
completions=sorted(completions)
166-
print 'COMPS',completions
167-
completions=[s+type_suffix(psymbols,s) for s in completions]
168-
return completions
169-
except:
170-
import traceback,sys
171-
traceback.print_exception(*sys.exc_info())
172-
def tool_tip(self,function):
173-
return False
174-
def tag_gen(self, projectDir):
175-
args = ['pysmell', projectDir, '-o', os.path.join(projectDir, 'PYSMELLTAGS')]
176-
sys.argv = args
177-
tags.main()
178-
return True
179-
def get_completions(self, cur_file, source, line_no, cur_col):
180-
"""arguments: fullPath, origSource, lineNo, origCol, matcher
181-
When visiting the file at fullPath, with edited source origSource, find a list
182-
of possible completion strings for the symbol located at origCol on orgLineNo using
183-
matching mode matcher"""
184-
print cur_file,line_no,cur_col
185-
if not cur_file:
186-
write('No filename - is the file saved?')
187-
return TOOLTIP
188-
source = file(cur_file,'r').read()
189-
190-
PYSMELLDICT = idehelper.findPYSMELLDICT(cur_file)
191-
if PYSMELLDICT is None:
192-
write('No PYSMELLTAGS found - you have to generate one.')
193-
return TOOLTIP
194-
print 'found dict'
195-
line = source.splitlines()[line_no - 1]
196-
index = idehelper.findBase(line, cur_col)
197-
base = line[index:cur_col]
198-
199-
options = idehelper.detectCompletionType(cur_file, source, line_no, cur_col, base, PYSMELLDICT)
200-
completions = idehelper.findCompletions(base, PYSMELLDICT, options)
201-
return completions
167+
calltip+='\n'+doc
168+
if calltip==None:
169+
calltip=='<unknown function>'
170+
return calltip
202171
def end(self):
203172
self._quit=True
204173
return True
@@ -210,7 +179,7 @@ def cmd_err():
210179

211180
if __name__=='__main__':
212181
if len(sys.argv)!=2:
213-
port=-1
182+
port=8001
214183
else:
215184
try:
216185
port = int(sys.argv[1])
@@ -219,5 +188,5 @@ def cmd_err():
219188
if port<-1:
220189
cmd_err()
221190

222-
server=AsyncServer(port)
191+
server=PyCompletionServer(port)
223192
server.run()

0 commit comments

Comments
 (0)