QTextCursor ScCodeEditor::selectionForPosition( int position ) { QTextBlock block( textDocument()->findBlock(position) ); if (!block.isValid()) return QTextCursor(); int positionInBlock = position - block.position(); TokenIterator it = TokenIterator( block, positionInBlock ); if (it.type() == Token::Unknown) { // Token is invalid, or Token::Unknown (i.e. punctuations). // Prefer token at previous position. TokenIterator alternativeIt = TokenIterator( block, positionInBlock - 1 ); if (alternativeIt.isValid()) it = alternativeIt; } if (it.isValid()) { switch (it->type) { case Token::OpeningBracket: case Token::ClosingBracket: { BracketPair match; matchBracket(it, match); if (match.first.isValid() && match.second.isValid()) { int start = match.first.position(); int end = match.second.position() + 1; QTextCursor selection(textDocument()); if (it == match.second) { selection.setPosition(start); selection.setPosition(end, QTextCursor::KeepAnchor); } else { selection.setPosition(end); selection.setPosition(start, QTextCursor::KeepAnchor); } return selection; } break; } default: QTextCursor selection( textDocument() ); selection.setPosition( it.position() ); selection.setPosition( selection.position() + it->length, QTextCursor::KeepAnchor ); return selection; } } return QTextCursor(); }
void ScCodeEditor::matchBrackets() { mBracketSelections.clear(); QTextCursor cursor(textCursor()); QTextBlock block( cursor.block() ); int posInBlock = cursor.positionInBlock(); TokenIterator it(block); while (it.isValid() && it.block() == block) { const Token & token = *it; if (token.positionInBlock > posInBlock) { it = TokenIterator(); break; } else if ( (token.positionInBlock == posInBlock && token.type == Token::OpeningBracket) || (token.positionInBlock == posInBlock - 1 && token.type == Token::ClosingBracket) ) break; ++it; } BracketPair match; if( it.isValid() && it.block() == block) matchBracket( it, match ); if( match.first.isValid() && match.second.isValid() ) { const Token & tok1 = *match.first; const Token & tok2 = *match.second; if ( (tok1.character == '(' && tok2.character == ')') || (tok1.character == '[' && tok2.character == ']') || (tok1.character == '{' && tok2.character == '}') ){ QTextEdit::ExtraSelection selection; selection.format = mBracketHighlight; cursor.setPosition(match.first.position()); cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); selection.cursor = cursor; mBracketSelections.append(selection); cursor.setPosition(match.second.position()); cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); selection.cursor = cursor; mBracketSelections.append(selection); if (mHighlightBracketContents) { QTextCharFormat format; format.setBackground( mBracketHighlight.background() ); selection.format = format; cursor.setPosition(match.first.position()+1); cursor.setPosition(match.second.position(), QTextCursor::KeepAnchor); selection.cursor = cursor; mBracketSelections.append(selection); } } else { QTextEdit::ExtraSelection selection; selection.format = mBracketMismatchFormat; cursor.setPosition(match.first.position()); cursor.setPosition(match.second.position()+1, QTextCursor::KeepAnchor); selection.cursor = cursor; mBracketSelections.append(selection); } } updateExtraSelections(); }
//FIXME: this has to be completely rewritten! ParserOutput* LaTeXParser::parse() { LaTeXParserOutput *parserOutput = new LaTeXParserOutput(); qCDebug(LOG_KILE_PARSER) << m_textLines; QMap<QString,KileStructData>::const_iterator it; static QRegExp reCommand("(\\\\[a-zA-Z]+)\\s*\\*?\\s*(\\{|\\[)"); static QRegExp reRoot("\\\\documentclass|\\\\documentstyle"); static QRegExp reBD("\\\\begin\\s*\\{\\s*document\\s*\\}"); static QRegExp reReNewCommand("\\\\renewcommand.*$"); static QRegExp reNumOfParams("\\s*\\[([1-9]+)\\]"); static QRegExp reNumOfOptParams("\\s*\\[([1-9]+)\\]\\s*\\[([^\\{]*)\\]"); // the quantifier * isn't used by mistake, because also emtpy optional brackets are correct. int tagStart, bd = 0; int tagEnd, tagLine = 0, tagCol = 0; int tagStartLine = 0, tagStartCol = 0; BracketResult result; QString m, s, shorthand; bool foundBD = false; // found \begin { document } bool fire = true; //whether or not we should emit a foundItem signal bool fireSuspended; // found an item, but it should not be fired (this time) TodoResult todo; // emit(parsingStarted(m_doc->lines())); for(int i = 0; i < m_textLines.size(); ++i) { if(!m_parserThread->shouldContinueDocumentParsing()) { qCDebug(LOG_KILE_PARSER) << "stopping..."; delete(parserOutput); return Q_NULLPTR; } // emit(parsingUpdate(i)); tagStart = tagEnd = 0; fire = true; s = processTextline(getTextLine(m_textLines, i), todo); if(todo.type != -1 && m_showStructureTodo) { QString folder = (todo.type == KileStruct::ToDo) ? "todo" : "fixme"; parserOutput->structureViewItems.push_back(new StructureViewItem(todo.comment, i+1, todo.colComment, todo.type, KileStruct::Object, i+1, todo.colTag, QString(), folder)); } if(s.isEmpty()) { continue; } //ignore renewcommands s.remove(reReNewCommand); //find all commands in this line while(tagStart != -1) { if((!foundBD) && ((bd = s.indexOf(reBD, tagEnd)) != -1)) { qCDebug(LOG_KILE_PARSER) << "\tfound \\begin{document}"; foundBD = true; parserOutput->preamble.clear(); //FIXME: improve this if(bd == 0) { if(i - 1 >= 0) { for(int j = 0; j <= i - 1; ++j) { parserOutput->preamble += getTextLine(m_textLines, j) + '\n'; } } } else { if(i - 1 >= 0) { for(int j = 0; j <= i - 1; ++j) { parserOutput->preamble += getTextLine(m_textLines, j) + '\n'; } } parserOutput->preamble += getTextLine(m_textLines, i).left(bd) + '\n'; } } if((!foundBD) && (s.indexOf(reRoot, tagEnd) != -1)) { qCDebug(LOG_KILE_PARSER) << "\tsetting m_bIsRoot to true"; tagEnd += reRoot.cap(0).length(); parserOutput->bIsRoot = true; } tagStart = reCommand.indexIn(s, tagEnd); m.clear(); shorthand.clear(); if(tagStart != -1) { tagEnd = tagStart + reCommand.cap(0).length()-1; //look up the command in the dictionary it = m_dictStructLevel.constFind(reCommand.cap(1)); //if it is was a structure element, find the title (or label) if(it != m_dictStructLevel.constEnd()) { tagLine = i+1; tagCol = tagEnd+1; tagStartLine = tagLine; tagStartCol = tagStart+1; if(reCommand.cap(1) != "\\frame") { result = matchBracket(m_textLines, i, tagEnd); m = result.value.trimmed(); shorthand = result.option.trimmed(); if(i >= tagLine) { //matching brackets spanned multiple lines s = getTextLine(m_textLines, i); } if(result.line > 0 || result.col > 0) { tagLine = result.line + 1; tagCol = result.col + 1; } //qCDebug(LOG_KILE_PARSER) << "\tgrabbed: " << reCommand.cap(1) << "[" << shorthand << "]{" << m << "}"; } else { m = i18n("Frame"); } } //title (or label) found, add the element to the listview if(!m.isNull()) { // no problems so far ... fireSuspended = false; // remove trailing ./ if((*it).type & (KileStruct::Input | KileStruct::Graphics)) { if(m.left(2) == "./") { m = m.mid(2, m.length() - 2); } } // update parameter for environments, because only // floating environments and beamer frames are passed if ( (*it).type == KileStruct::BeginEnv ) { if ( m=="figure" || m=="figure*" || m=="table" || m=="table*" ) { it = m_dictStructLevel.constFind("\\begin{" + m +'}'); } else if(m == "asy") { it = m_dictStructLevel.constFind("\\begin{" + m +'}'); parserOutput->asyFigures.append(m); } else if(m == "frame") { const QString untitledFrameDisplayName = i18n("Frame"); it = m_dictStructLevel.constFind("\\begin{frame}"); if(tagEnd+1 < s.size() && s.at(tagEnd+1) == '{') { tagEnd++; result = matchBracket(m_textLines, i, tagEnd); m = result.value.trimmed(); if(m.isEmpty()) { m = untitledFrameDisplayName; } } else { m = untitledFrameDisplayName; } } else if(m=="block" || m=="exampleblock" || m=="alertblock") { const QString untitledBlockDisplayName = i18n("Untitled Block"); it = m_dictStructLevel.constFind("\\begin{block}"); if(tagEnd+1 < s.size() && s.at(tagEnd+1) == '{') { tagEnd++; result = matchBracket(m_textLines, i, tagEnd); m = result.value.trimmed(); if(m.isEmpty()) { m = untitledBlockDisplayName; } } else { m = untitledBlockDisplayName; } } else { fireSuspended = true; // only floats and beamer frames, no other environments } } // tell structure view that a floating environment or a beamer frame must be closed else if ( (*it).type == KileStruct::EndEnv ) { if ( m=="figure" || m== "figure*" || m=="table" || m=="table*" || m=="asy") { it = m_dictStructLevel.constFind("\\end{float}"); } else if(m == "frame") { it = m_dictStructLevel.constFind("\\end{frame}"); } else { fireSuspended = true; // only floats, no other environments } } // sectioning commands else if((*it).type == KileStruct::Sect) { if(!shorthand.isEmpty()) { m = shorthand; } } // update the label list else if((*it).type == KileStruct::Label) { parserOutput->labels.append(m); // label entry as child of sectioning if(m_showSectioningLabels) { parserOutput->structureViewItems.push_back(new StructureViewItem(m, tagLine, tagCol, KileStruct::Label, KileStruct::Object, tagStartLine, tagStartCol, "label", "root")); fireSuspended = true; } } // update the references list else if((*it).type == KileStruct::Reference) { // m_references.append(m); //fireSuspended = true; // don't emit references } // update the dependencies else if((*it).type == KileStruct::Input) { // \input- or \include-commands can be used without extension. So we check // if an extension exists. If not the default extension is added // ( LaTeX reference says that this is '.tex'). This assures that // all files, which are listed in the structure view, have an extension. QString ext = QFileInfo(m).completeSuffix(); if(ext.isEmpty()) { m += m_extensions->latexDocumentDefault(); } parserOutput->deps.append(m); } // update the referenced Bib files else if((*it).type == KileStruct::Bibliography) { qCDebug(LOG_KILE_PARSER) << "===TeXInfo::updateStruct()===appending Bibiliograph file(s) " << m; QStringList bibs = m.split(','); QString biblio; // assure that all files have an extension QString bibext = m_extensions->bibtexDefault(); int bibextlen = bibext.length(); uint cumlen = 0; int nextbib = 0; // length to add to jump to the next bibliography for(int b = 0; b < bibs.count(); ++b) { nextbib = 0; biblio=bibs[b]; parserOutput->bibliography.append(biblio); if(biblio.left(2) == "./") { nextbib += 2; biblio = biblio.mid(2, biblio.length() - 2); } if(biblio.right(bibextlen) != bibext) { biblio += bibext; nextbib -= bibextlen; } parserOutput->deps.append(biblio); parserOutput->structureViewItems.push_back(new StructureViewItem(biblio, tagLine, tagCol+cumlen, (*it).type, (*it).level, tagStartLine, tagStartCol, (*it).pix, (*it).folder)); cumlen += biblio.length() + 1 + nextbib; } fire = false; } // update the bibitem list else if((*it).type == KileStruct::BibItem) { //qCDebug(LOG_KILE_PARSER) << "\tappending bibitem " << m; parserOutput->bibItems.append(m); } // update the package list else if((*it).type == KileStruct::Package) { QStringList pckgs = m.split(','); uint cumlen = 0; for(int p = 0; p < pckgs.count(); ++p) { QString package = pckgs[p].trimmed(); if(!package.isEmpty()) { parserOutput->packages.append(package); // hidden, so emit is useless // emit( foundItem(package, tagLine, tagCol+cumlen, (*it).type, (*it).level, tagStartLine, tagStartCol, (*it).pix, (*it).folder) ); cumlen += package.length() + 1; } } fire = false; } // newcommand found, add it to the newCommands list else if((*it).type & (KileStruct::NewCommand | KileStruct::NewEnvironment)) { QString optArg, mandArgs; //find how many parameters this command takes if(s.indexOf(reNumOfParams, tagEnd + 1) != -1) { bool ok; int noo = reNumOfParams.cap(1).toInt(&ok); if(ok) { if(s.indexOf(reNumOfOptParams, tagEnd + 1) != -1) { qCDebug(LOG_KILE_PARSER) << "Opt param is " << reNumOfOptParams.cap(2) << "%EOL"; noo--; // if we have an opt argument, we have one mandatory argument less, and noo=0 can't occur because then latex complains (and we don't macht them with reNumOfParams either) optArg = '[' + reNumOfOptParams.cap(2) + ']'; } for(int noo_index = 0; noo_index < noo; ++noo_index) { mandArgs += '{' + s_bullet + '}'; } } if(!optArg.isEmpty()) { if((*it).type == KileStruct::NewEnvironment) { parserOutput->newCommands.append(QString("\\begin{%1}%2%3").arg(m).arg(optArg).arg(mandArgs)); } else { parserOutput->newCommands.append(m + optArg + mandArgs); } } } if((*it).type == KileStruct::NewEnvironment) { parserOutput->newCommands.append(QString("\\begin{%1}%3").arg(m).arg(mandArgs)); parserOutput->newCommands.append(QString("\\end{%1}").arg(m)); } else { parserOutput->newCommands.append(m + mandArgs); } //FIXME set tagEnd to the end of the command definition break; } // and some other commands, which don't need special actions: // \caption, ... // qCDebug(LOG_KILE_PARSER) << "\t\temitting: " << m; if(fire && !fireSuspended) { parserOutput->structureViewItems.push_back(new StructureViewItem(m, tagLine, tagCol, (*it).type, (*it).level, tagStartLine, tagStartCol, (*it).pix, (*it).folder)); } } //if m } // if tagStart } // while tagStart } //for qCDebug(LOG_KILE_PARSER) << "done"; return parserOutput; }