18 #include "QsciNCLAPIs.h"
20 #include "NCLTextEditor.h"
22 #include <QFileDialog>
24 #include <core/util/Utilities.h>
25 using namespace composer::core::util;
27 QsciNCLAPIs::QsciNCLAPIs(QsciLexer * lexer) :
30 nclStructure = NCLStructure::getInstance();
32 add (QString(
"ncl30"));
36 QsciNCLAPIs::~QsciNCLAPIs()
44 suggesting = SUGGESTING_OTHER;
46 int pos = lexer()->editor()
47 ->SendScintilla(QsciScintilla::SCI_GETSELECTIONSTART);
48 qDebug() <<
"updateAutoCompletionList to";
52 suggesting = SUGGESTING_ELEMENTS;
53 qDebug() <<
"Must suggest elements" << endl;
54 for (
int i = 0; i < context.count(); ++i) {
55 qDebug() <<
"'" << context[i] <<
"'";
56 QString father = getParentTagName(pos);
57 qDebug() <<
"Father = " << father;
61 map <QString, char> *elements = nclStructure->
getChildren(father);
62 map <QString, char>::iterator it;
66 it = elements->begin();
67 for (; it != elements->end(); ++it)
69 if(it->first.startsWith(context[i]))
71 QString str(it->first);
73 qDebug() << it->first;
79 else if ( isAttribute(pos) )
81 suggesting = SUGGESTING_ATTRIBUTES;
82 QString tagname = getCurrentTagName(pos);
83 QStringList current_attrs;
85 getAttributesTyped(pos, current_attrs);
86 for (
int i = 0; i < current_attrs.size(); i++)
87 qDebug() <<
"Already typed: " << current_attrs.at(i);
89 qDebug() <<
"Must suggest attributes to " << tagname;
92 map <QString, bool> *attrs = nclStructure->
getAttributes(tagname);
93 deque <QString> *attrs_ordered =
97 deque <QString>::iterator it;
98 for (
int i = 0; i < context.count(); ++i)
100 it = attrs_ordered->begin();
102 for (; it != attrs_ordered->end(); ++it){
103 if( it->startsWith(context[i])
104 && !current_attrs.contains(*it))
115 else if ( isAttributeValue(pos) )
117 suggesting = SUGGESTING_ATTRIBUTE_VALUES;
118 QString tagname = getCurrentTagName(pos);
119 QString attribute = getCurrentAttribute(pos);
120 QString datatype = nclStructure->getAttributeDatatype(tagname, attribute);
123 qDebug() << tagname <<
":" << attribute <<
" -> datatype=" << datatype;
125 QStringList defaultSuggestion =
126 nclStructure->getDatatypeDefaultSuggestions(datatype);
128 list.append(defaultSuggestion);
130 vector <AttributeReferences *>
131 references = nclStructure->
getReferences(tagname, attribute);
133 if(datatype ==
"URI")
135 QString filename = QFileDialog::getOpenFileName(nclEditor,
138 Utilities::getLastFileDialogPath());
140 if(!filename.isEmpty() && !filename.isNull())
142 Utilities::updateLastFileDialogPath(filename);
146 filename = relativePath(nclEditor->getDocumentUrl(), filename,
true);
151 nclEditor->removeSelectedText();
153 if(nclEditor->SendScintilla(QsciScintilla::SCI_GETCHARAT, pos) ==
' ')
155 nclEditor->SendScintilla(QsciScintilla::SCI_GOTOPOS, pos+1);
156 nclEditor->SendScintilla(QsciScintilla::SCI_DELETEBACK);
159 nclEditor->insert(filename);
163 if(references.size())
167 if(nclEditor->parseDocument())
170 for(
unsigned int i = 0; i < references.size(); i++)
172 qDebug() <<
"Should refer to " << references[i]->getRefElement()
173 <<
"." << references[i]->getRefAttribute()
174 <<
"in the scope: " << references[i]->getScope();
176 QList <QDomElement> elements;
179 if(references[i]->getScope() == AttributeReferences::ANY_SCOPE)
181 elements = nclEditor->
182 elementsByTagname(references[i]->getRefElement());
184 else if(references[i]->getScope() == AttributeReferences::SAME_SCOPE)
186 int parent_offset = getParentOffset(pos);
187 while(!nclStructure->defineScope(getCurrentTagName(parent_offset)))
189 parent_offset = getParentOffset(parent_offset);
193 getAttributeValueFromCurrentElement(parent_offset,
"id");
196 elements = nclEditor->
197 elementsByTagname( references[i]->getRefElement(),
200 else if(references[i]->getScope() ==
201 AttributeReferences::USERDEFINED_SCOPE)
204 QString userDefinedScope = references[i]->getUserDefinedScope();
206 qDebug() <<
"User defined scope" << userDefinedScope;
209 if(userDefinedScope.startsWith(
"$THIS"))
211 attr = userDefinedScope.mid(6);
212 qDebug() <<
"$THIS" << attr;
213 QString idValue = getAttributeValueFromCurrentElement(pos, attr);
214 elements = nclEditor->elementsByTagname(
215 references[i]->getRefElement(), idValue);
217 else if(userDefinedScope.startsWith(
"$PARENT"))
219 attr = userDefinedScope.mid(8);
220 qDebug() <<
"$PARENT" << attr;
221 int parent_offset = getParentOffset(pos);
222 qDebug() <<
"parent_offset" << parent_offset;
223 QString idValue = getAttributeValueFromCurrentElement(
224 parent_offset, attr);
226 qDebug() <<
"idValue = " << idValue;
227 elements = nclEditor->elementsByTagname(
228 references[i]->getRefElement(), idValue);
231 else if(userDefinedScope.startsWith(
"$GRANDPARENT"))
233 attr = userDefinedScope.mid(13);
234 qDebug() <<
"$GRANDPARENT" << attr;
235 int grandparent_offset = getParentOffset(getParentOffset(pos));
236 QString idValue = getAttributeValueFromCurrentElement(
237 grandparent_offset, attr);
238 elements = nclEditor->elementsByTagname(
239 references[i]->getRefElement(), idValue);
243 for(
int j = 0; j < elements.length(); j++)
245 QDomElement node = elements.at(j).toElement();
247 QString attributeValue =
248 node.attribute(references[i]->getRefAttribute());
250 qDebug() << context.count();
251 for (
int k = 0; k < context.count(); ++k)
253 if(attributeValue.startsWith(context[k]))
254 list.append(attributeValue);
262 qDebug() <<
"Coud not parse the document";
265 list.removeDuplicates();
271 int start = 0, line, pos;
272 QString outputStr(
"");
274 bool fixidentation =
false;
275 bool hasAttributes =
false;
278 lexer()->editor()->beginUndoAction();
280 lexer()->editor()->SendScintilla(QsciScintilla::SCI_AUTOCCANCEL);
281 lexer()->editor()->getCursorPosition(&line, &pos);
284 strline = lexer()->editor()->text( line );
288 if(start >= 0 && strline.at(start).isLetter())
290 lexer()->editor()->SendScintilla( QsciScintilla::SCI_DELWORDLEFT );
293 lexer()->editor()->getCursorPosition(&line, &pos);
294 strline = lexer()->editor()->text(line);
298 if(suggesting == SUGGESTING_ELEMENTS)
300 QString attributes = getRequiredAttributesAsStr(selection);
303 hasAttributes =
true;
305 if(start < 0 || strline.at(start) !=
'<')
307 outputStr += selection +
" " + attributes;
309 map <QString, char> *children = nclStructure->
getChildren(selection);
310 if ( children != NULL && children->size() ) {
314 outputStr += selection;
316 fixidentation =
true;
323 else if(suggesting == SUGGESTING_ATTRIBUTES)
325 outputStr = selection +
"=\"\"";
329 QChar ch = strline.at(start);
332 outputStr.prepend(
' ');
335 else if(suggesting == SUGGESTING_ATTRIBUTE_VALUES)
337 int ps = lexer()->editor()->SendScintilla(QsciScintilla::SCI_GETCURRENTPOS)
339 int pe = lexer()->editor()->SendScintilla(QsciScintilla::SCI_GETCURRENTPOS);
340 bool quote1_found =
false, quote2_found =
false;
344 lexer()->editor()->SendScintilla(
345 QsciScintilla::SCI_GETLINESELSTARTPOSITION, ps);
347 lexer()->editor()->SendScintilla(QsciScintilla::SCI_GETLINEENDPOSITION,
350 while(ps >= startline && !quote1_found){
351 ch = lexer()->editor()->SendScintilla(QsciScintilla::SCI_GETCHARAT, ps);
352 if(ch ==
'\'' || ch ==
'\"')
357 while(pe <= endline && !quote2_found){
358 ch = lexer()->editor()->SendScintilla(QsciScintilla::SCI_GETCHARAT, pe);
359 if(ch ==
'\'' || ch ==
'\"')
364 qDebug() << quote1_found << quote2_found;
366 if(quote1_found && quote2_found)
368 lexer()->editor()->SendScintilla(QsciScintilla::SCI_SETSELECTIONSTART,
370 lexer()->editor()->SendScintilla(QsciScintilla::SCI_SETSELECTIONEND,
372 lexer()->editor()->removeSelectedText();
375 outputStr = selection;
379 pos = lexer()->editor()->SendScintilla(QsciScintilla::SCI_GETCURRENTPOS);
380 lexer()->editor()->insert(outputStr);
384 int lineident = lexer()->editor()->SendScintilla (
385 QsciScintilla::SCI_GETLINEINDENTATION,
388 lexer()->editor()->SendScintilla( QsciScintilla::SCI_SETLINEINDENTATION,
394 lexer()->editor()->SendScintilla(QsciScintilla::SCI_WORDRIGHT);
395 lexer()->editor()->SendScintilla(QsciScintilla::SCI_WORDRIGHT);
397 lexer()->editor()->endUndoAction();
399 if (suggesting == SUGGESTING_ELEMENTS)
403 lexer()->editor()->recolor();
404 ((
NCLTextEditor *)lexer()->editor())->userFillingNextAttribute(pos);
411 QsciScintilla::CallTipsStyle style,
414 (void) context; (void) commas; (void) style; (void) shifts;
423 bool QsciNCLAPIs::event(QEvent *e)
430 QString QsciNCLAPIs::getRequiredAttributesAsStr(
const QString &element)
433 map <QString, bool> *attributes = NCLStructure::getInstance()
434 ->getAttributes(element);
435 deque <QString> *attrs_ordered = NCLStructure::getInstance()
436 ->getAttributesOrdered(element);
438 if(attributes != NULL && attrs_ordered != NULL) {
439 deque <QString>::iterator it;
441 for(it = attrs_ordered->begin(); it != attrs_ordered->end(); ++it){
443 if(attributes->count(str) && (*attributes)[str]) {
447 ret += *it +
"=\"\"";
454 bool QsciNCLAPIs::isElement(
int pos)
456 int style = lexer()->editor()
457 ->SendScintilla( QsciScintilla:: SCI_GETSTYLEAT, pos);
458 qDebug() <<
"Style=" << style;
459 if(style == QsciLexerNCL::Default)
463 else if( style == QsciLexerNCL::Tag ||
464 style == QsciLexerNCL::XMLTagEnd ||
465 style == QsciLexerNCL::XMLStart ||
466 style == QsciLexerNCL::OtherInTag ||
467 style == QsciLexerNCL::UnknownTag)
472 char ch = lexer()->editor()
473 ->SendScintilla( QsciScintilla::SCI_GETCHARAT, p);
474 char previous_ch = lexer()->editor()
475 ->SendScintilla( QsciScintilla::SCI_GETCHARAT, p-1);
480 if(previous_ch ==
'/')
492 bool QsciNCLAPIs::isAttribute(
int pos)
494 qDebug() <<
"QsciNCLAPIs::isAttribute";
495 int style = lexer()->editor()->SendScintilla(QsciScintilla::SCI_GETSTYLEAT,
497 if ( style == QsciLexerNCL::Attribute )
499 else if( style == QsciLexerNCL::Tag ||
500 style == QsciLexerNCL::XMLTagEnd ||
501 style == QsciLexerNCL::XMLStart ||
502 style == QsciLexerNCL::OtherInTag ||
503 style == QsciLexerNCL::UnknownTag){
506 char ch = lexer()->editor()
508 QsciScintilla::SCI_GETCHARAT,
511 if(isspace(ch))
return true;
512 if(ch ==
'<')
return true;
519 QString QsciNCLAPIs::getCurrentTagName(
int pos)
522 char ch = lexer()->editor()->SendScintilla(QsciScintilla::SCI_GETCHARAT, p);
525 while (p >= 0 && ch !=
'<'){
527 ch = lexer()->editor()->SendScintilla( QsciScintilla::SCI_GETCHARAT,
533 ch = lexer()->editor()->SendScintilla( QsciScintilla::SCI_GETCHARAT,
539 ch = lexer()->editor()
540 ->SendScintilla( QsciScintilla::SCI_GETCHARAT,
549 ch = lexer()->editor()->SendScintilla( QsciScintilla::SCI_GETCHARAT,
551 while(p >= 0 && isalnum(ch)){
554 ch = lexer()->editor()
555 ->SendScintilla( QsciScintilla::SCI_GETCHARAT,
558 if(ch ==
'<')
return word;
566 QString QsciNCLAPIs::getCurrentAttribute (
int pos)
570 QString current_attribute =
"";
571 if (isAttributeValue(pos))
573 bool quote_found =
false, equal_found =
false;
575 while(p >= 0 && !quote_found){
576 ch = lexer()->editor()->SendScintilla( QsciScintilla::SCI_GETCHARAT,
578 if(ch ==
'\'' || ch ==
'\"')
583 while(p >= 0 && !equal_found){
584 ch = lexer()->editor()->SendScintilla( QsciScintilla::SCI_GETCHARAT,
592 ch = lexer()->editor()->SendScintilla( QsciScintilla::SCI_GETCHARAT,
600 ch = lexer()->editor()->SendScintilla( QsciScintilla::SCI_GETCHARAT,
604 current_attribute.prepend(ch);
608 current_attribute = current_attribute.trimmed();
611 return current_attribute;
615 int QsciNCLAPIs::getParentOffset(
int pos)
618 char ch = lexer()->editor()->SendScintilla(QsciScintilla::SCI_GETCHARAT, p);
625 ch = lexer()->editor()->SendScintilla(QsciScintilla::SCI_GETCHARAT, p);
627 reading = QsciLexerNCL::XMLStart;
630 ch = lexer()->editor()->SendScintilla(QsciScintilla::SCI_GETCHARAT, p);
632 reading = QsciLexerNCL::XMLTagEnd;
634 reading = QsciLexerNCL::HTMLComment;
637 while (p >= 0 && ch !=
'<'){
640 ch = lexer()->editor()
641 ->SendScintilla(QsciScintilla::SCI_GETCHARAT, p);
643 if(lastch ==
'/') reading = QsciLexerNCL::XMLEnd;
645 if(reading == QsciLexerNCL::XMLEnd)
647 else if(reading == QsciLexerNCL::XMLStart)
650 qDebug() <<
"closed_tags = " << closed_tags <<
" p=" << p;
659 QString QsciNCLAPIs::getAttributeValueFromCurrentElement(
int pos, QString attr)
661 qDebug() <<
"QsciNCLAPIs::getAttributeValueFromCurrentElement";
662 int start = getStartTagBegin(pos);
663 int length = getStartTagLength(pos);
664 int end = start + length;
665 QString attrValue =
"";
667 if(start < 0 || end < 0)
return "";
670 char *chars = (
char *) malloc ((end - start) *
sizeof(char) + 1);
671 lexer()->editor()->SendScintilla( QsciScintilla::SCI_GETTEXTRANGE,
675 text = QString(chars);
676 qDebug() <<
"text = " << text;
680 QString tagname = getCurrentTagName(pos);
686 if(domDoc.setContent(text))
688 attrValue = domDoc.firstChildElement().attribute(attr);
694 QString QsciNCLAPIs::getParentTagName(
int pos)
696 int p_offset = getParentOffset(pos);
698 return getCurrentTagName(p_offset);
704 bool QsciNCLAPIs::isAttributeValue(
int pos)
706 qDebug() <<
"QsciNCLAPIs::isAttributeValue";
707 int style = lexer()->editor()
708 ->SendScintilla( QsciScintilla:: SCI_GETSTYLEAT,
711 if ( style == QsciLexerNCL::HTMLDoubleQuotedString
712 || style == QsciLexerNCL::HTMLSingleQuotedString )
720 int QsciNCLAPIs::getStartTagBegin(
int pos)
723 char ch = lexer()->editor()->SendScintilla(QsciScintilla::SCI_GETCHARAT, p);
724 while (p >= 0 && ch !=
'<'){
726 ch = lexer()->editor()->SendScintilla(QsciScintilla::SCI_GETCHARAT, p);
732 int QsciNCLAPIs::getStartTagLength(
int pos)
734 int start = getStartTagBegin(pos);
736 int text_length = lexer()->editor()
737 ->SendScintilla(QsciScintilla::SCI_GETLENGTH);
738 char ch = lexer()->editor()->SendScintilla(QsciScintilla::SCI_GETCHARAT, p);
740 while (p < text_length && ch != '>
'){
742 ch = lexer()->editor()->SendScintilla(QsciScintilla::SCI_GETCHARAT, p);
744 return p >= text_length ? -1 : (p-start+1);
747 void QsciNCLAPIs::getAttributesTyped(int pos, QStringList &attrs)
749 int start = getStartTagBegin(pos);
750 int length = getStartTagLength(pos);
751 int end = start + length;
753 qDebug() << "start=" << start << " end=" << end;
755 if(start < 0 || end < 0) return;
758 char *chars = (char *) malloc ((end - start) * sizeof(char) + 1);
759 lexer()->editor()->SendScintilla( QsciScintilla::SCI_GETTEXTRANGE,
763 text = QString(chars);
764 qDebug() << "text = " << text;
766 //FIXME: The following regex is not completely correct. Te text inside
767 // the attribute will be matched in some cases.
768 QRegExp attrRegex ("\\s[a-zA-Z]+");
771 int index = attrRegex.indexIn(text, lastIndex);
773 if (index + attrRegex.matchedLength() > end)
775 qDebug() << index << " " << attrRegex.matchedLength();
776 attrs << text.mid(index, attrRegex.matchedLength()).trimmed();
778 lastIndex = (index + attrRegex.matchedLength());
779 index = attrRegex.indexIn(chars, lastIndex);
784 QString QsciNCLAPIs::relativePath( QString absolutePath, QString relativeTo,
785 bool bIsFile /*= false*/ )
787 QStringList absoluteDirectories = absolutePath.split( '/
', QString::SkipEmptyParts );
788 QStringList relativeDirectories = relativeTo.split( '/
', QString::SkipEmptyParts );
790 //Get the shortest of the two paths
791 int length = absoluteDirectories.count() < relativeDirectories.count() ? absoluteDirectories.count() : relativeDirectories.count();
793 //Use to determine where in the loop we exited
794 int lastCommonRoot = -1;
798 for (index = 0; index < length; index++)
799 if (absoluteDirectories[index] == relativeDirectories[index])
800 lastCommonRoot = index;
804 //If we didn't find a common prefix then
throw
805 if (lastCommonRoot == -1)
806 throw QString(
"Paths do not have a common base");
809 QString relativePath;
812 for (index = lastCommonRoot + 1; index < absoluteDirectories.count() - (bIsFile?1:0); index++)
813 if (absoluteDirectories[index].length() > 0)
814 relativePath.append(
"../");
817 for (index = lastCommonRoot + 1; index < relativeDirectories.count() - 1; index++)
818 relativePath.append( relativeDirectories[index] ).append(
"/" );
819 relativePath.append(relativeDirectories[relativeDirectories.count() - 1]);