]>
git.saurik.com Git - cycript.git/blob - Complete.cpp
820895c9c91776af12b040caace6244758274367
1 /* Cycript - The Truly Universal Scripting Language
2 * Copyright (C) 2009-2016 Jay Freeman (saurik)
5 /* GNU Affero General Public License, Version 3 {{{ */
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "cycript.hpp"
25 #include "Replace.hpp"
28 static CYExpression
*ParseExpression(CYPool
&pool
, CYUTF8String code
) {
29 std::stringstream stream
;
30 stream
<< '(' << code
<< ')';
31 CYDriver
driver(pool
, *stream
.rdbuf());
32 if (driver
.Parse() || !driver
.errors_
.empty())
36 CYContext
context(options
);
38 CYStatement
*statement(driver
.script_
->code_
);
39 _assert(statement
!= NULL
);
40 _assert(statement
->next_
== NULL
);
42 CYExpress
*express(dynamic_cast<CYExpress
*>(driver
.script_
->code_
));
43 _assert(express
!= NULL
);
45 CYParenthetical
*parenthetical(dynamic_cast<CYParenthetical
*>(express
->expression_
));
46 _assert(parenthetical
!= NULL
);
48 return parenthetical
->expression_
;
51 _visible
char **CYComplete(const char *word
, const std::string
&line
, CYUTF8String (*run
)(CYPool
&pool
, const std::string
&)) {
54 std::stringbuf
stream(line
);
55 CYDriver
driver(pool
, stream
);
59 if (driver
.Parse() || !driver
.errors_
.empty())
62 if (driver
.mode_
== CYDriver::AutoNone
)
65 CYExpression
*expression
;
68 CYContext
context(options
);
70 std::ostringstream prefix
;
72 switch (driver
.mode_
) {
73 case CYDriver::AutoPrimary
:
74 expression
= $
CYThis();
77 case CYDriver::AutoDirect
:
78 expression
= driver
.context_
;
81 case CYDriver::AutoIndirect
:
82 expression
= $
CYIndirect(driver
.context_
);
85 case CYDriver::AutoMessage
: {
86 CYDriver::Context
&thing(driver
.contexts_
.back());
87 expression
= $
M($
C1($
V("object_getClass"), thing
.context_
), $
S("messages"));
88 for (CYDriver::Context::Words::const_iterator
part(thing
.words_
.begin()); part
!= thing
.words_
.end(); ++part
)
89 prefix
<< (*part
)->word_
<< ':';
92 case CYDriver::AutoResolve
:
93 expression
= $
M(driver
.context_
, $
S("$cyr"));
96 case CYDriver::AutoStruct
:
97 expression
= $
CYThis();
101 case CYDriver::AutoEnum
:
102 expression
= $
CYThis();
110 std::string
begin(prefix
.str());
112 driver
.script_
= $
CYScript($
CYExpress($
C3(ParseExpression(pool
,
113 " function(object, prefix, word) {\n"
115 " var before = prefix.length;\n"
117 " var entire = prefix.length;\n"
119 " for (var name in object)\n"
120 " if (name.substring(0, entire) == prefix)\n"
121 " names.push(name);\n"
123 " if (object.hasOwnProperty(\"cy$complete\")) {\n"
124 " names = names.concat(object.cy$complete(prefix));\n"
128 " var local = Object.getOwnPropertyNames(object);\n"
132 " for (var name of local)\n"
133 " if (name.substring(0, entire) == prefix)\n"
134 " names.push(name);\n"
135 " } while (object = typeof object === 'object' ? Object.getPrototypeOf(object) : object.__proto__);\n"
138 ), expression
, $
S(begin
.c_str()), $
S(word
))));
140 driver
.script_
->Replace(context
);
143 CYOutput
out(str
, options
);
144 out
<< *driver
.script_
;
146 std::string
code(str
.str());
147 CYUTF8String
json(run(pool
, code
));
148 // XXX: if this fails we should not try to parse it
150 CYExpression
*result(ParseExpression(pool
, json
));
154 CYArray
*array(dynamic_cast<CYArray
*>(result
->Primitive(context
)));
158 // XXX: use an std::set?
159 typedef std::vector
<CYUTF8String
> Completions
;
160 Completions completions
;
165 for (CYElement
*element(array
->elements_
); element
!= NULL
; ) {
166 CYElementValue
*value(dynamic_cast<CYElementValue
*>(element
));
167 _assert(value
!= NULL
);
168 element
= value
->next_
;
170 CYString
*string(dynamic_cast<CYString
*>(value
->value_
));
171 _assert(string
!= NULL
);
173 CYUTF8String completion
;
174 if (string
->size_
!= 0)
175 completion
= {string
->value_
, string
->size_
};
176 else if (driver
.mode_
== CYDriver::AutoMessage
)
181 completion
.data
+= begin
.size();
182 completion
.size
-= begin
.size();
184 if (CYStartsWith(completion
, "$cy"))
186 completions
.push_back(completion
);
192 size_t limit(completion
.size
), size(common
.size());
194 common
= common
.substr(0, limit
);
197 for (limit
= 0; limit
!= size
; ++limit
)
198 if (common
[limit
] != completion
.data
[limit
])
201 common
= common
.substr(0, limit
);
205 size_t count(completions
.size());
209 size_t colon(common
.find(':'));
210 if (colon
!= std::string::npos
)
211 common
= common
.substr(0, colon
+ 1);
212 if (completions
.size() == 1)
215 char **results(reinterpret_cast<char **>(malloc(sizeof(char *) * (count
+ 2))));
217 results
[0] = strdup(common
.c_str());
219 for (Completions::const_iterator
i(completions
.begin()); i
!= completions
.end(); ++i
)
220 results
[++index
] = strdup(i
->data
);
221 results
[count
+ 1] = NULL
;