scide: implement selectionLength for openDocument
[supercollider.git] / SCClassLibrary / scide_scqt / ScIDE.sc
blob329231874cff4b679941f38c637729accf05e401
1 ScIDE {
2         classvar subListSorter;
3         classvar <>currentPath;
4         classvar <defaultServer;
6         *initClass {
7                 subListSorter = { | a b | a[0].perform('<', b[0]) };
9                 Class.initClassTree(Server);
11                 StartUp.add {
12                         if (ScIDE.connected) {
13                                 this.handshake
14                         };
15                 }
16         }
18         *connect {|ideName|
19                 this.prConnect(ideName);
20                 defer {
21                         this.handshake
22                 }
23         }
25         *handshake {
26                 this.prSend(\classLibraryRecompiled);
27                 this.prSend(\requestCurrentPath);
29                 defaultServer = Server.default;
31                 SimpleController(defaultServer)
32                 .put(\serverRunning, { | server, what, extraArg |
33                         var addr = server.addr;
34                         this.prSend(\defaultServerRunningChanged, [server.serverRunning, addr.hostname, addr.port])
35                 });
37                 this.prSend(\defaultServerRunningChanged, [defaultServer.serverRunning, defaultServer.addr.hostname, defaultServer.addr.port]);
39                 this.sendIntrospection; // sending the introspection at the end, as otherwise the communication channel seems to get stuck
40         }
42         *request { |id, command, data|
43                 this.tryPerform(command, id, data);
44         }
46         *connected {
47                 _ScIDE_Connected
48         }
50         *open { |path, charPos = 0, selectionLength = 0|
51                 this.prSend(\openFile, [path, charPos, selectionLength])
52         }
54         *sendIntrospection {
55                 var out;
56                 out = [];
57                 Class.allClasses.do { |class|
58                         var classData;
59                         classData = [
60                                 class.name,
61                                 class.class.name,
62                                 class.superclass !? {class.superclass.name},
63                                 class.filenameSymbol,
64                                 class.charPos,
65                                 class.methods.collect { |m| this.serializeMethodDetailed(m) };
66                         ];
67                         out = out.add(classData);
68                 };
69                 this.prSend(\introspection, out);
70         }
72         *sendAllClasses { |id|
73                 this.prSend(id, Class.allClasses.collectAs(_.asString, Array))
74         }
76         *sendSymbolTable { |id|
77                 var result, dt;
79                 dt = {
80                         result = IdentitySet(16384);
81                         Class.allClasses.do { | class |
82                                 if (class.isMetaClass.not) {
83                                         result.add(class.name);
84                                 };
85                                 class.methods.do { | method |
86                                         result.add(method.name);
87                                 };
88                         };
90                         result = result.collectAs(_.asString, Array)
91                 }.bench(false);
93                 "ScIDE: Built symbol table in % seconds\n".postf(dt.asStringPrec(3));
95                 this.prSend(id, result)
96         }
98         *completeClass { |id, text|
99                 var out = [];
100                 Class.allClasses.do { |class|
101                         var name = class.name.asString;
102                         if (name.beginsWith(text)) {
103                                 out = out.add(name);
104                         };
105                 };
106                 if (out.size > 0) {
107                         this.prSend(id, out);
108                 };
109         }
111         *completeClassMethod { |id, text|
112                 var class, methods, out;
113                 class = text.asSymbol.asClass;
114                 if (class.notNil) {
115                         methods = IdentityDictionary();
116                         class = class.class;
117                         while { class.notNil } {
118                                 class.methods.do { |method|
119                                         // methods include operators like "+", but those are
120                                         // actually not valid in the method call syntax
121                                         if (method.name.asString[0].isAlpha &&
122                                                 methods[method.name].isNil)
123                                         {
124                                                 methods.put(method.name, method);
125                                         };
126                                 };
127                                 class = class.superclass;
128                         };
129                         out = methods.values.collect { |m| this.serializeMethod(m) };
130                         if (out.size > 0) { this.prSend(id, out) };
131                 }
132         }
134         *completeMethod { |id, text|
135                 var out;
136                 out = [];
137                 Class.allClasses.do { |class|
138                         class.methods.do { |method|
139                                 var signature;
140                                 var definition;
141                                 if (method.name.asString.beginsWith(text)) {
142                                         out = out.add( this.serializeMethod(method) );
143                                 };
144                         };
145                 };
146                 if (out.size > 0) { this.prSend(id, out) };
147         }
149         *findMethod { |id, text|
150                 var cname, mname, tokens, out;
151                 var class, method;
153                 tokens = text.split($.);
154                 if (tokens.size > 1) {
155                         cname = tokens[0];
156                         mname = tokens[1];
157                 }{
158                         mname = tokens[0];
159                 };
160                 if (mname.size < 1) { ^this };
162                 if (cname.size > 0) {
163                         class = cname.asSymbol.asClass;
164                         if (class.isNil) {
165                                 warn("No class named" + cname.asString);
166                                 ^this;
167                         };
168                         method = class.class.findRespondingMethodFor(mname.asSymbol);
169                         if (method.isNil) {
170                                 warn("No such method:" + cname.asString ++ "." ++ mname.asString);
171                                 ^this;
172                         };
173                         this.prSend(id, [this.serializeMethod(method)]);
174                 }{
175                         out = [];
176                         this.allMethodsDo { |method|
177                                 if (method.name.asString == mname) {
178                                         out = out.add( this.serializeMethod(method) );
179                                 };
180                         };
181                         if (out.size > 0) {
182                                 this.prSend(id, out)
183                         }{
184                                 warn("No such method:" + mname.asString);
185                                 ^this;
186                         };
187                 }
188         }
190         *serializeMethod { arg method;
191                 var data = [method.ownerClass.name, method.name];
192                 if (method.argNames.size > 1) {
193                         data = data ++ [
194                                 method.argNames.as(Array),
195                                 method.prototypeFrame.collect { |val| val !? val.cs }
196                         ].lace [2..];
197                 };
198                 ^data;
199         }
201         *serializeMethodDetailed { arg method;
202                 var args, data;
203                 args = [];
204                 if (method.argNames.size > 1) {
205                         args = args ++ [
206                                 method.argNames.as(Array),
207                                 method.prototypeFrame.collect { |val|
208                                         val !? {
209                                                 if (val.class === Float) { val.asString } { val.cs }
210                                         }
211                                 };
212                         ].lace [2..];
213                 };
214                 data = [
215                         method.ownerClass.name,
216                         method.name,
217                         method.filenameSymbol,
218                         method.charPos,
219                         args
220                 ];
221                 ^data;
222         }
224         *allMethodsDo { arg func;
225                 Class.allClasses.do { |class|
226                         class.methods.do { |method|
227                                 func.value(method);
228                         };
229                 };
230         }
232         *findReferencesToSymbol {|requestId, symbol|
233                 var methods;
234                 var result = SortedList(8, subListSorter);
235                 var references = Class.findAllReferences(symbol.asSymbol);
237                 if (references.notNil) {
238                         methods = IdentitySet.new;
239                         references.do { | funcDef |
240                                 var homeContext;
241                                 homeContext = if(funcDef.context.isNil) {funcDef} {funcDef.context.homeContext};
242                                 if (homeContext.isKindOf(Method)) {
243                                         methods.add(homeContext);
244                                 };
245                         };
246                         methods.do { | method |
247                                 result.add([
248                                         method.ownerClass.name,
249                                         method.name,
250                                         method.filenameSymbol.asString,
251                                         method.charPos + 1
252                                 ])
253                         }
254                 };
256                 ScIDE.prSend(requestId, [symbol, result.asArray])
257         }
259         *prSend {|id, data|
260                 _ScIDE_Send
261                 this.primitiveFailed
262         }
264         *prConnect {|ideName|
265                 _ScIDE_Connect
266                 this.primitiveFailed
267         }