1 /*
2 SPDX-License-Identifier: GPL-2.0-or-later
3 SPDX-FileCopyrightText: 2009 Alexander Rieder <alexanderrieder@gmail.com>
4 */
5
6 #include "sagecompletionobject.h"
7
8 #include "sagesession.h"
9 #include "sagekeywords.h"
10 #include "textresult.h"
11
12 #include <QDebug>
13 #include <QStack>
14
15 using namespace Cantor;
16
SageCompletionObject(const QString & command,int index,SageSession * session)17 SageCompletionObject::SageCompletionObject(const QString& command, int index, SageSession* session) : Cantor::CompletionObject(session)
18 {
19 setLine(command, index);
20 m_expression=nullptr;
21 }
22
~SageCompletionObject()23 SageCompletionObject::~SageCompletionObject()
24 {
25 if(m_expression)
26 m_expression->setFinishingBehavior(Expression::DeleteOnFinish);
27 }
28
fetchCompletions()29 void SageCompletionObject::fetchCompletions()
30 {
31 if (session()->status() != Cantor::Session::Done)
32 {
33 QStringList allCompletions;
34
35 allCompletions << SageKeywords::instance()->keywords();
36 allCompletions << SageKeywords::instance()->functions();
37 allCompletions << SageKeywords::instance()->variables();
38
39 setCompletions(allCompletions);
40 emit fetchingDone();
41 }
42 else
43 {
44 if (m_expression)
45 return;
46
47 //cache the value of the "_" variable into __hist_tmp__, so we can restore the previous result
48 //after complete() was evaluated
49 const QString& cmd = QLatin1String("__hist_tmp__=_; sage.interfaces.tab_completion.completions(\"")+command()+QLatin1String("\",globals());_=__hist_tmp__");
50 m_expression=session()->evaluateExpression(cmd, Cantor::Expression::FinishingBehavior::DoNotDelete, true);
51 connect(m_expression, &Cantor::Expression::gotResult, this, &SageCompletionObject::extractCompletions);
52 }
53 }
54
extractCompletions()55 void SageCompletionObject::extractCompletions()
56 {
57 SageSession* s=qobject_cast<SageSession*>(session());
58 if(s&&s->sageVersion()<SageSession::VersionInfo(5, 7))
59 extractCompletionsLegacy();
60 else
61 extractCompletionsNew();
62 }
63
extractCompletionsNew()64 void SageCompletionObject::extractCompletionsNew()
65 {
66 Cantor::Result* res=m_expression->result();
67 m_expression->deleteLater();
68 m_expression=nullptr;
69
70 if(!res || !(res->type()==Cantor::TextResult::Type))
71 {
72 qDebug()<<"something went wrong fetching tab completion";
73 fetchingDone();
74 return;
75 }
76
77 //the result looks like "['comp1', 'comp2']" parse it
78
79 QString txt=res->data().toString().trimmed();
80 txt=txt.mid(1); //remove [
81 txt.chop(1); //remove ]
82
83 qDebug()<<"completion string: "<<txt;
84
85 QStringList tmp=txt.split(QLatin1Char(','));
86 QStringList completions;
87
88 foreach(QString c, tmp) // krazy:exclude=foreach
89 {
90 c=c.trimmed();
91 c.chop(1);
92 completions<<c.mid(1);
93 }
94
95 completions << SageKeywords::instance()->keywords();
96 setCompletions(completions);
97
98 emit fetchingDone();
99 }
100
extractCompletionsLegacy()101 void SageCompletionObject::extractCompletionsLegacy()
102 {
103 Cantor::Result* res=m_expression->result();
104 m_expression->deleteLater();
105 m_expression=nullptr;
106
107 if(!res || !(res->type()==Cantor::TextResult::Type))
108 {
109 qDebug()<<"something went wrong fetching tab completion";
110 fetchingDone();
111 return;
112 }
113
114 //the result looks like "['comp1', 'comp2']" parse it
115 QString txt=res->data().toString().trimmed();
116 txt=txt.mid(1); //remove [
117 txt.chop(1); //remove ]
118
119 QStringList tmp=txt.split(QLatin1Char(','));
120 QStringList completions;
121
122 foreach(QString c, tmp) // krazy:exclude=foreach
123 {
124 c=c.trimmed();
125 c.chop(1);
126 completions<<c.mid(1);
127 }
128
129 completions << SageKeywords::instance()->keywords();
130 setCompletions(completions);
131
132 emit fetchingDone();
133 }
134
135
fetchIdentifierType()136 void SageCompletionObject::fetchIdentifierType()
137 {
138 if (SageKeywords::instance()->keywords().contains(identifier()))
139 {
140 emit fetchingTypeDone(KeywordType);
141 return;
142 }
143
144 if (session()->status() != Cantor::Session::Done)
145 {
146 if (SageKeywords::instance()->functions().contains(identifier()))
147 emit fetchingTypeDone(FunctionType);
148 else if (SageKeywords::instance()->variables().contains(identifier()))
149 emit fetchingTypeDone(VariableType);
150 else
151 emit fetchingTypeDone(UnknownType);
152 }
153 else
154 {
155 if (m_expression)
156 return;
157
158 QString expr = QString::fromLatin1("__cantor_internal__ = _; type(%1); _ = __cantor_internal__").arg(identifier());
159 m_expression = session()->evaluateExpression(expr, Cantor::Expression::FinishingBehavior::DoNotDelete, true);
160 connect(m_expression, &Cantor::Expression::statusChanged, this, &SageCompletionObject::extractIdentifierType);
161 }
162 }
163
extractIdentifierType(Cantor::Expression::Status status)164 void SageCompletionObject::extractIdentifierType(Cantor::Expression::Status status)
165 {
166 switch(status)
167 {
168 case Cantor::Expression::Error:
169 qDebug() << "Error with SageCompletionObject" << m_expression->errorMessage();
170 emit fetchingTypeDone(UnknownType);
171 break;
172
173 case Cantor::Expression::Interrupted:
174 qDebug() << "SageCompletionObject was interrupted";
175 emit fetchingTypeDone(UnknownType);
176 break;
177
178 case Cantor::Expression::Done:
179 {
180 Cantor::Result* result = m_expression->result();
181 if (result)
182 {
183 QString res = result->data().toString();
184 if (res.contains(QLatin1String("function")) || res.contains(QLatin1String("method")))
185 emit fetchingTypeDone(FunctionType);
186 else
187 emit fetchingTypeDone(VariableType);
188 }
189 else
190 emit fetchingTypeDone(UnknownType);
191 break;
192 }
193
194 default:
195 return;
196 }
197
198 m_expression->deleteLater();
199 m_expression = nullptr;
200 }
201
mayIdentifierContain(QChar c) const202 bool SageCompletionObject::mayIdentifierContain(QChar c) const
203 {
204 return c.isLetter() || c.isDigit() || c == QLatin1Char('_') || c == QLatin1Char('.');
205 }
206
mayIdentifierBeginWith(QChar c) const207 bool SageCompletionObject::mayIdentifierBeginWith(QChar c) const
208 {
209 return c.isLetter() || c.isDigit() || c == QLatin1Char('_');
210 }
211