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