/********************************************************************************************* begin : Sun Jul 20 2003 copyright : (C) 2003 by Jeroen Wijnhout (Jeroen.Wijnhout@kdemail.net) (C) 2005-2007 by Holger Danielsson (holger.danielsson@versanet.de) (C) 2006 by Michel Ludwig (michel.ludwig@kdemail.net) *********************************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ // 2005-11-02: dani // - cleaning up source of central function updateStruct() // - always use 'else if', because all conditions are exclusive or // - most often used commands are at the top // - add some new types of elements (and levels) for the structure view // - new commands, which are passed to the structure listview: // \includegraphics, \caption // - all user defined commands for labels are recognized // - changed folder name of KileStruct::BibItem to "bibs", so that "refs" // is still unused and can be used for references (if wanted) // - \begin, \end to gather all environments. But only figure and table // environments are passed to the structure view // 2005-11-26: dani // - add support for \fref, \Fref and \eqref references commands // 2005-12-07: dani // - add support to enable and disable some structure view items // 2006-01-16 tbraun // - fix #59945 Now we call (through a signal ) project->buildProjectTree so the bib files are correct, // and therefore the keys in \cite completion // 2006-02-09 tbraun/dani // - fix #106261#4 improved parsing of (optional) command parameters // - all comments are removed //2006-09-09 mludwig // - generalising the different document types //2007-02-15 // - signal foundItem() not only sends the cursor position of the parameter, // but also the real cursor position of the command // 2007-03-12 dani // - use KileDocument::Extensions // 2007-03-24 dani // - preliminary minimal support for Beamer class // 2007-03-25 dani // - merge labels and sections in document structure view as user configurable option // 2007-04-06 dani // - add TODO/FIXME section to structure view #include "kiledocumentinfo.h" #include #include #include #include #include #include #include #include #include #include "kiledebug.h" #include #include #include #include "codecompletion.h" #include "kileuntitled.h" #include "kileconfig.h" namespace KileDocument { bool Info::containsInvalidCharacters(const KURL& url) { TQString filename = url.fileName(); return filename.contains(" ") || filename.contains("~") || filename.contains("$") || filename.contains("#"); } KURL Info::repairInvalidCharacters(const KURL& url, bool checkForFileExistence /* = true */) { KURL ret(url); do { bool isOK; TQString newURL = KInputDialog::getText( i18n("Invalid Characters"), i18n("The filename contains invalid characters ($~ #).
Please provide \ another one, or click \"Cancel\" to save anyway."), ret.filename(), &isOK); if(!isOK) break; ret.setFileName(newURL); } while(containsInvalidCharacters(ret)); return (checkForFileExistence ? renameIfExist(ret) : ret); } KURL Info::renameIfExist(const KURL& url) { KURL ret(url); while ( TDEIO::NetAccess::exists(url, true, kapp->mainWidget()) ) // check for writing possibility { bool isOK; TQString newURL = KInputDialog::getText( i18n("File Already Exists"), i18n("A file with filename '%1' already exists.
Please provide \ another one, or click \"Cancel\" to overwrite it.").arg(ret.fileName()), ret.filename(), &isOK); if(!isOK) break; ret.setFileName(newURL); } return ret; } KURL Info::repairExtension(const KURL& url, bool checkForFileExistence /* = true */) { KURL ret(url); TQString filename = url.fileName(); if(filename.contains(".") && filename[0] != '.') // There already is an extension return ret; if(KMessageBox::Yes == KMessageBox::questionYesNo(NULL, i18n("The given filename has no extension; do you want one to be automatically added?"), i18n("Missing Extension"), KStdGuiItem::yes(), KStdGuiItem::no(), "AutomaticallyAddExtension")) { ret.setFileName(filename + ".tex"); } return (checkForFileExistence ? renameIfExist(ret) : ret); } KURL Info::makeValidTeXURL(const KURL & url, bool istexfile, bool checkForFileExistence) { KURL newURL(url); //add a .tex extension if ( ! istexfile ) newURL = repairExtension(newURL, checkForFileExistence); //remove characters TeX does not accept, make sure the newURL does not exists yet if(containsInvalidCharacters(newURL)) newURL = repairInvalidCharacters(newURL, checkForFileExistence); return newURL; } Info::Info() : m_bIsRoot(false), m_config(kapp->config()), documentTypePromotionAllowed(true) { updateStructLevelInfo(); } Info::~Info(void) { KILE_DEBUG() << "DELETING DOCINFO" << m_url.path() << endl; } void Info::updateStructLevelInfo() { KILE_DEBUG() << "===void Info::updateStructLevelInfo()===" << endl; // read config for structureview items m_showStructureLabels = KileConfig::svShowLabels(); m_showStructureReferences = KileConfig::svShowReferences(); m_showStructureBibitems = KileConfig::svShowBibitems(); m_showStructureGraphics = KileConfig::svShowGraphics(); m_showStructureFloats = KileConfig::svShowFloats(); m_showStructureInputFiles = KileConfig::svShowInputFiles(); m_showStructureTodo = KileConfig::svShowTodo(); m_showSectioningLabels = KileConfig::svShowSectioningLabels(); m_openStructureLabels = KileConfig::svOpenLabels(); m_openStructureReferences = KileConfig::svOpenReferences(); m_openStructureBibitems = KileConfig::svOpenBibitems(); m_openStructureTodo = KileConfig::svOpenTodo(); } void Info::setBaseDirectory(const KURL& url) { KILE_DEBUG() << "===void Info::setBaseDirectory(const KURL&" << url << ")===" << endl; m_baseDirectory = url; } const KURL& Info::getBaseDirectory() const { return m_baseDirectory; } bool Info::isTextDocument() { return false; } Type Info::getType() { return Undefined; } TQString Info::getFileFilter() const { return TQString(); } bool Info::isDocumentTypePromotionAllowed() { return documentTypePromotionAllowed; } void Info::setDocumentTypePromotionAllowed(bool b) { documentTypePromotionAllowed = b; } KURL Info::url() { return m_url; } void Info::count(const TQString line, long *stat) { TQChar c; int state = stStandard; bool word = false; // we are in a word for (uint p=0; p < line.length(); ++p) { c = line[p]; switch ( state ) { case stStandard : switch ( c ) { case TEX_CAT0 : state = stControlSequence; ++stat[1]; //look ahead to avoid counting words like K\"ahler as two words if (! line[p+1].isPunct() || line[p+1] == '~' || line[p+1] == '^' ) word=false; break; case TEX_CAT14 : state=stComment; break; default: if (c.isLetterOrNumber()) { //only start new word if first character is a letter (42test is still counted as a word, but 42.2 not) if (c.isLetter() && !word) { word=true; ++stat[3]; } ++stat[0]; } else { ++stat[2]; word = false; } break; } break; case stControlSequence : if ( c.isLetter() ) { // "\begin{[a-zA-z]+}" is an environment, and you can't define a command like \begin if ( line.mid(p,5) == "begin" ) { ++stat[5]; state = stEnvironment; stat[1] +=5; p+=4; // after break p++ is executed } else if ( line.mid(p,3) == "end" ) { stat[1] +=3; state = stEnvironment; p+=2; } // we don't count \end as new environment, this can give wrong results in selections else { ++stat[4]; ++stat[1]; state = stCommand; } } else { ++stat[4]; ++stat[1]; state = stStandard; } break; case stCommand : if ( c.isLetter() ) ++stat[1]; else if ( c == TEX_CAT0 ) { ++stat[1]; state=stControlSequence; } else if ( c == TEX_CAT14 ) state=stComment; else { ++stat[2]; state = stStandard; } break; case stEnvironment : if ( c == TEX_CAT2 ) // until we find a closing } we have an environment { ++stat[1]; state=stStandard; } else if ( c == TEX_CAT14 ) state=stComment; else ++stat[1]; break; case stComment : // if we get a selection the line possibly contains \n and so the comment is only valid till \n and not necessarily till line.length() if ( c == '\n') { ++stat[2]; // \n was counted as punctuation in the old implementation state=stStandard; word=false; } break; default : kdWarning() << "Unhandled state in getStatistics " << state << endl; break; } } } TQString Info::lastModifiedFile(const TQStringList *list /* = 0L */) { KILE_DEBUG() << "==TQString Info::lastModifiedFile()=====" << endl; TQFileInfo fileinfo ( url().path() ); TQString basepath = fileinfo.dirPath(true), last = fileinfo.absFilePath(); TQDateTime time ( fileinfo.lastModified() ); if ( list == 0L ) list = &m_deps; KILE_DEBUG() << "\t" << fileinfo.absFilePath() << " : " << time.toString() << endl; for ( uint i = 0; i < list->count(); ++i ) { fileinfo.setFile( basepath + '/' + (*list)[i] ); KILE_DEBUG() << "\t" << fileinfo.absFilePath() << " : " << fileinfo.lastModified().toString() << endl; if ( fileinfo.lastModified() > time ) { time = fileinfo.lastModified(); last = fileinfo.absFilePath(); KILE_DEBUG() << "\t\tlater" << endl; } } KILE_DEBUG() << "\treturning " << fileinfo.absFilePath() << endl; return last; } void Info::updateStruct() { KILE_DEBUG() << "==Info::updateStruct()=======" << endl; m_labels.clear(); m_bibItems.clear(); m_deps.clear(); m_bibliography.clear(); m_packages.clear(); m_newCommands.clear(); m_bIsRoot = false; m_preamble = TQString(); } void Info::updateBibItems() { } void Info::slotCompleted() { emit completed(this); } TextInfo::TextInfo(Kate::Document *doc, Extensions *extensions, const TQString& defaultHighlightMode) : m_doc(0), m_defaultHighlightMode(defaultHighlightMode) { setDoc(doc); if (m_doc) { KILE_DEBUG() << "TextInfo created for " << m_doc->docName() << endl; } m_arStatistics = new long[SIZE_STAT_ARRAY]; if (m_doc) { m_url = doc->url(); } else { m_url = KURL(); } m_extensions = extensions; } TextInfo::~TextInfo() { delete [] m_arStatistics; } const Kate::Document* TextInfo::getDoc() const { return m_doc; } Kate::Document* TextInfo::getDoc() { return m_doc; } void TextInfo::setDoc(Kate::Document *doc) { KILE_DEBUG() << "===void TextInfo::setDoc(Kate::Document *doc)===" << endl; if(m_doc == doc) return; detach(); if(doc) { m_doc = doc; m_url = doc->url(); connect(m_doc, TQT_SIGNAL(fileNameChanged()), this, TQT_SLOT(slotFileNameChanged())); connect(m_doc, TQT_SIGNAL(completed()), this, TQT_SLOT(slotCompleted())); setHighlightMode(m_defaultHighlightMode); installEventFilters(); } } void TextInfo::detach() { if(m_doc) { m_doc->disconnect(this); removeInstalledEventFilters(); } m_doc = 0L; } const long* TextInfo::getStatistics() { /* [0] = #c in words, [1] = #c in latex commands and environments, [2] = #c whitespace, [3] = #words, [4] = # latex_commands, [5] = latex_environments */ m_arStatistics[0]=m_arStatistics[1]=m_arStatistics[2]=m_arStatistics[3]=m_arStatistics[4]=m_arStatistics[5]=0; TQString line; if ( m_doc && m_doc->hasSelection() ) { line = m_doc->selection(); KILE_DEBUG() << "getStat : line : " << line << endl; count(line, m_arStatistics); } else if (m_doc) { for (uint l=0; l < m_doc->numLines(); ++l) { line = m_doc->textLine(l); KILE_DEBUG() << "getStat : line : " << line << endl; count(line, m_arStatistics); } } return m_arStatistics; } // FIXME for KDE 4.0, rearrange the hole docinfo layout to get rid of this hack KURL TextInfo::url() { KURL url; if(m_doc) url = m_doc->url(); else { TQFileInfo info(m_url.path()); if(info.exists()) url = m_url; else url = KURL(); } // KILE_DEBUG() << "===KURL TextInfo::url()===, url is " << url.path() << endl; return url; } Type TextInfo::getType() { return Text; } bool TextInfo::isTextDocument() { return true; } void TextInfo::setHighlightMode(const TQString &highlight) { KILE_DEBUG() << "==Kile::setHighlightMode(" << m_doc->url() << "," << highlight << " )==================" << endl; if ( m_doc && !highlight.isEmpty() ) { bool found = false; uint c = m_doc->hlModeCount(); uint mode = 0; for (uint i = 0; i < c; ++i) { if (m_doc->hlModeName(i) == highlight) { found = true; mode = i; break; } } if (found) { m_doc->setHlMode(mode); } } } void TextInfo::setDefaultHightlightMode(const TQString& string) { m_defaultHighlightMode = string; } // match a { with the corresponding } // pos is the position of the { TQString TextInfo::matchBracket(TQChar obracket, uint &l, uint &pos) { TQChar cbracket; if ( obracket == '{' ) cbracket = '}'; if ( obracket == '[' ) cbracket = ']'; if ( obracket == '(' ) cbracket = ')'; TQString line, grab = ""; int count=0, len; ++pos; TodoResult todo; while ( l <= m_doc->numLines() ) { line = getTextline(l,todo); len = line.length(); for (int i=pos; i < len; ++i) { if (line[i] == '\\' && ( line[i+1] == obracket || line[i+1] == cbracket) ) ++i; else if (line[i] == obracket) ++count; else if (line[i] == cbracket) { --count; if (count < 0) { pos = i; return grab; } } grab += line[i]; } ++l; pos=0; } return TQString(); } TQString TextInfo::getTextline(uint line, TodoResult &todo) { static TQRegExp reComments("[^\\\\](%.*$)"); todo.type = -1; TQString s = m_doc->textLine(line); if ( ! s.isEmpty() ) { // remove comment lines if ( s[0] == '%' ) { searchTodoComment(s,0,todo); s = TQString(); } else { //remove escaped \ characters s.replace("\\\\", " "); //remove comments int pos = s.find(reComments); if ( pos != -1 ) { searchTodoComment(s,pos,todo); s = s.left(reComments.pos(1)); } } } return s; } void TextInfo::searchTodoComment(const TQString &s, uint startpos, TodoResult &todo) { static TQRegExp reTodoComment("\\b(TODO|FIXME)\\b(:|\\s)?\\s*(.*)"); if ( s.find(reTodoComment,startpos) != -1 ) { todo.type = ( reTodoComment.cap(1) == "TODO" ) ? KileStruct::ToDo : KileStruct::FixMe; todo.colTag = reTodoComment.pos(1); todo.colComment = reTodoComment.pos(3); todo.comment = reTodoComment.cap(3).stripWhiteSpace(); } } KTextEditor::View* TextInfo::createView(TQWidget *parent, const char *name) { if(!m_doc) { return NULL; } KTextEditor::View *view = m_doc->createView(parent, name); installEventFilters(view); return view; } void TextInfo::slotFileNameChanged() { emit urlChanged(this, url()); } void TextInfo::installEventFilters(KTextEditor::View* /* view */) { /* do nothing */ } void TextInfo::removeInstalledEventFilters(KTextEditor::View* /* view */) { /* do nothing */ } void TextInfo::installEventFilters() { if(!m_doc) { return; } TQPtrList views = m_doc->views(); KTextEditor::View *view; for(view = views.first(); view; view = views.next()) { installEventFilters(view); } } void TextInfo::removeInstalledEventFilters() { if(!m_doc) { return; } TQPtrList views = m_doc->views(); KTextEditor::View *view; for(view = views.first(); view; view = views.next()) { removeInstalledEventFilters(view); } } LaTeXInfo::LaTeXInfo (Kate::Document *doc, Extensions *extensions, LatexCommands *commands, const TQObject* eventFilter) : TextInfo(doc, extensions, "LaTeX"), m_commands(commands), m_eventFilter(eventFilter) { documentTypePromotionAllowed = false; updateStructLevelInfo(); } LaTeXInfo::~LaTeXInfo() { } Type LaTeXInfo::getType() { return LaTeX; } TQString LaTeXInfo::getFileFilter() const { return m_extensions->latexDocumentFileFilter() + '\n' + m_extensions->latexPackageFileFilter(); } void LaTeXInfo::updateStructLevelInfo() { KILE_DEBUG() << "===void LaTeXInfo::updateStructLevelInfo()===" << endl; // read config stuff Info::updateStructLevelInfo(); // clear all entries m_dictStructLevel.clear(); //TODO: make sectioning and bibliography configurable // sectioning m_dictStructLevel["\\part"]=KileStructData(1, KileStruct::Sect, "part"); m_dictStructLevel["\\chapter"]=KileStructData(2, KileStruct::Sect, "chapter"); m_dictStructLevel["\\section"]=KileStructData(3, KileStruct::Sect, "section"); m_dictStructLevel["\\subsection"]=KileStructData(4, KileStruct::Sect, "subsection"); m_dictStructLevel["\\subsubsection"]=KileStructData(5, KileStruct::Sect, "subsubsection"); m_dictStructLevel["\\paragraph"]=KileStructData(6, KileStruct::Sect, "subsubsection"); m_dictStructLevel["\\subparagraph"]=KileStructData(7, KileStruct::Sect, "subsubsection"); m_dictStructLevel["\\bibliography"]=KileStructData(0,KileStruct::Bibliography, "viewbib"); // hidden commands m_dictStructLevel["\\usepackage"]=KileStructData(KileStruct::Hidden, KileStruct::Package); m_dictStructLevel["\\newcommand"]=KileStructData(KileStruct::Hidden, KileStruct::NewCommand); m_dictStructLevel["\\newlength"]=KileStructData(KileStruct::Hidden, KileStruct::NewCommand); m_dictStructLevel["\\newenvironment"]=KileStructData(KileStruct::Hidden, KileStruct::NewEnvironment); m_dictStructLevel["\\addunit"]=KileStructData(KileStruct::Hidden, KileStruct::NewCommand); // hack to get support for the fancyunits package until we can configure the commands in the gui (tbraun) m_dictStructLevel["\\DeclareMathOperator"]=KileStructData(KileStruct::Hidden, KileStruct::NewCommand); // amsmath package m_dictStructLevel["\\caption"]=KileStructData(KileStruct::Hidden,KileStruct::Caption); // bibitems if ( m_showStructureBibitems ) { m_dictStructLevel["\\bibitem"]= KileStructData(KileStruct::NotSpecified, KileStruct::BibItem, TQString(), "bibs"); } // graphics if ( m_showStructureGraphics ) { m_dictStructLevel["\\includegraphics"]=KileStructData(KileStruct::Object,KileStruct::Graphics, "graphics"); } // float environments if ( m_showStructureFloats ) { m_dictStructLevel["\\begin"]=KileStructData(KileStruct::Object,KileStruct::BeginEnv); m_dictStructLevel["\\end"]=KileStructData(KileStruct::Hidden,KileStruct::EndEnv); // some entries, which could never be found (but they are set manually) m_dictStructLevel["\\begin{figure}"]=KileStructData(KileStruct::Object,KileStruct::BeginFloat, "frame_image"); m_dictStructLevel["\\begin{figure*}"]=KileStructData(KileStruct::Object,KileStruct::BeginFloat, "frame_image"); m_dictStructLevel["\\begin{table}"]=KileStructData(KileStruct::Object,KileStruct::BeginFloat, "frame_spreadsheet"); m_dictStructLevel["\\end{float}"]=KileStructData(KileStruct::Hidden,KileStruct::EndFloat); } // preliminary minimal beamer support m_dictStructLevel["\\frame"]=KileStructData(KileStruct::Object, KileStruct::BeamerFrame, "beamerframe"); m_dictStructLevel["\\frametitle"]=KileStructData(KileStruct::Hidden, KileStruct::BeamerFrametitle); m_dictStructLevel["\\begin{frame}"]=KileStructData(KileStruct::Object, KileStruct::BeamerBeginFrame, "beamerframe"); m_dictStructLevel["\\end{frame}"]=KileStructData(KileStruct::Hidden, KileStruct::BeamerEndFrame); m_dictStructLevel["\\begin{block}"]=KileStructData(KileStruct::Object, KileStruct::BeamerBeginBlock, "beamerblock"); // add user defined commands TQStringList list; TQStringList::ConstIterator it; // labels, we also gather them m_commands->commandList(list,KileDocument::CmdAttrLabel,false); for ( it=list.begin(); it != list.end(); ++it ) m_dictStructLevel[*it]= KileStructData(KileStruct::NotSpecified, KileStruct::Label, TQString(), "labels"); // input files if ( m_showStructureInputFiles ) { m_commands->commandList(list,KileDocument::CmdAttrIncludes,false); for ( it=list.begin(); it != list.end(); ++it ) m_dictStructLevel[*it]= KileStructData(KileStruct::File, KileStruct::Input, "include"); } // references if ( m_showStructureReferences ) { m_commands->commandList(list,KileDocument::CmdAttrReference,false); for ( it=list.begin(); it != list.end(); ++it ) m_dictStructLevel[*it]= KileStructData(KileStruct::Hidden, KileStruct::Reference); } } void LaTeXInfo::installEventFilters(KTextEditor::View *view) { view->focusProxy()->installEventFilter(const_cast(m_eventFilter)); } void LaTeXInfo::removeInstalledEventFilters(KTextEditor::View *view) { view->focusProxy()->removeEventFilter(const_cast(m_eventFilter)); } BracketResult LaTeXInfo::matchBracket(uint &l, uint &pos) { BracketResult result; TodoResult todo; if ( m_doc->textLine(l)[pos] == '[' ) { result.option = TextInfo::matchBracket('[', l, pos); int p = 0; while ( l < m_doc->numLines() ) { if ( (p = getTextline(l,todo).find('{', pos)) != -1 ) { pos = p; break; } else { pos = 0; ++l; } } } if ( m_doc->textLine(l)[pos] == '{' ) { result.line = l; result.col = pos; result.value = TextInfo::matchBracket('{', l, pos); } return result; } //FIXME refactor, clean this mess up void LaTeXInfo::updateStruct() { KILE_DEBUG() << "==void TeXInfo::updateStruct: (" << url() << ")=========" << endl; if ( getDoc() == 0L ) return; Info::updateStruct(); TQMapConstIterator it; static TQRegExp reCommand("(\\\\[a-zA-Z]+)\\s*\\*?\\s*(\\{|\\[)"); static TQRegExp reRoot("\\\\documentclass|\\\\documentstyle"); static TQRegExp reBD("\\\\begin\\s*\\{\\s*document\\s*\\}"); static TQRegExp reReNewCommand("\\\\renewcommand.*$"); static TQRegExp reNumOfParams("\\s*\\[([1-9]+)\\]"); static TQRegExp reNumOfOptParams("\\s*\\[([1-9]+)\\]\\s*\\[([^\\{]*)\\]"); // the quantifier * isn't used by mistake, because also emtpy optional brackets are correct. int teller=0, tagStart, bd = 0; uint tagEnd, tagLine = 0, tagCol = 0; uint tagStartLine = 0, tagStartCol = 0; BracketResult result; TQString 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; for(uint i = 0; i < m_doc->numLines(); ++i) { if (teller > 100) { teller=0; kapp->processEvents(); } else ++teller; tagStart=tagEnd=0; fire = true; s = getTextline(i,todo); if ( todo.type!=-1 && m_showStructureTodo ) { TQString folder = ( todo.type == KileStruct::ToDo ) ? "todo" : "fixme"; emit( foundItem(todo.comment, i+1, todo.colComment, todo.type, KileStruct::Object, i+1, todo.colTag, TQString(), folder) ); } if ( s.isEmpty() ) continue; //ignore renewcommands s.replace(reReNewCommand, ""); //find all commands in this line while (tagStart != -1) { if ( (!foundBD) && ( (bd = s.find(reBD, tagEnd)) != -1)) { KILE_DEBUG() << "\tfound \\begin{document}" << endl; foundBD = true; if ( bd == 0 ) m_preamble = m_doc->text(0, 0, i - 1, m_doc->textLine(i - 1).length() ); else m_preamble = m_doc->text(0, 0, i, bd); } if ((!foundBD) && (s.find(reRoot, tagEnd) != -1)) { KILE_DEBUG() << "\tsetting m_bIsRoot to true" << endl; tagEnd += reRoot.cap(0).length(); m_bIsRoot = true; } tagStart = reCommand.search(s,tagEnd); m=TQString(); shorthand = TQString(); if (tagStart != -1) { tagEnd = tagStart + reCommand.cap(0).length()-1; //look up the command in the dictionary it = m_dictStructLevel.find(reCommand.cap(1)); //if it is was a structure element, find the title (or label) if (it != m_dictStructLevel.end()) { tagLine = i+1; tagCol = tagEnd+1; tagStartLine = tagLine; tagStartCol = tagStart+1; if ( reCommand.cap(1) != "\\frame" ) { result = matchBracket(i, static_cast(tagEnd)); m = result.value.stripWhiteSpace(); shorthand = result.option.stripWhiteSpace(); if ( i >= tagLine ) //matching brackets spanned multiple lines s = m_doc->textLine(i); if ( result.line>0 || result.col>0 ) { tagLine = result.line + 1; tagCol = result.col + 1; } //KILE_DEBUG() << "\tgrabbed: " << reCommand.cap(1) << "[" << shorthand << "]{" << m << "}" << endl; } 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" ) { it = m_dictStructLevel.find("\\begin{" + m +'}'); } else if ( m == "frame" ) { it = m_dictStructLevel.find("\\begin{frame}"); m = i18n("Frame"); } else if ( m=="block" || m=="exampleblock" || m=="alertblock") { const TQString untitledBlockDisplayName = i18n("Untitled Block"); it = m_dictStructLevel.find("\\begin{block}"); if ( s.at(tagEnd+1) == '{' ) { tagEnd++; result = matchBracket(i, static_cast(tagEnd)); m = result.value.stripWhiteSpace(); 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") { it = m_dictStructLevel.find("\\end{float}"); } else if ( m == "frame" ) { it = m_dictStructLevel.find("\\end{frame}"); } else fireSuspended = true; // only floats, no other environments } // sectioning commands else if ( (*it).type == KileStruct::Sect ) { if ( ! shorthand.isNull() ) m = shorthand; } // update the label list else if ( (*it).type == KileStruct::Label ) { m_labels.append(m); // label entry as child of sectioning if ( m_showSectioningLabels ) { emit( foundItem(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. TQString ext = TQFileInfo(m).extension(); if ( ext.isEmpty() ) m += m_extensions->latexDocumentDefault(); m_deps.append(m); } // update the referenced Bib files else if( (*it).type == KileStruct::Bibliography ) { KILE_DEBUG() << "===TeXInfo::updateStruct()===appending Bibiliograph file(s) " << m << endl; TQStringList bibs = TQStringList::split(",", m); TQString biblio; // assure that all files have an extension TQString bibext = m_extensions->bibtexDefault(); int bibextlen = bibext.length(); uint cumlen = 0; int nextbib = 0; // length to add to jump to the next bibliography for (uint b = 0; b < bibs.count(); ++b) { nextbib = 0; biblio=bibs[b]; m_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; } m_deps.append(biblio); emit( foundItem(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 ) { //KILE_DEBUG() << "\tappending bibitem " << m << endl; m_bibItems.append(m); } // update the package list else if ( (*it).type == KileStruct::Package ) { TQStringList pckgs = TQStringList::split(",", m); uint cumlen = 0; for (uint p = 0; p < pckgs.count(); ++p) { TQString package = pckgs[p].stripWhiteSpace(); if ( ! package.isEmpty() ) { m_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 ) ) { TQString optArg, mandArgs; //find how many parameters this command takes if ( s.find(reNumOfParams, tagEnd + 1) != -1 ) { bool ok; int noo = reNumOfParams.cap(1).toInt(&ok); if ( ok ) { if(s.find(reNumOfOptParams, tagEnd + 1) != -1) { KILE_DEBUG() << "Opt param is " << reNumOfOptParams.cap(2) << "%EOL" << endl; 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) { m_newCommands.append(TQString("\\begin{%1}%2%3").arg(m).arg(optArg).arg(mandArgs)); } else m_newCommands.append(m + optArg + mandArgs); } } if( (*it).type == KileStruct::NewEnvironment) { m_newCommands.append(TQString("\\begin{%1}%3").arg(m).arg(mandArgs)); m_newCommands.append(TQString("\\end{%1}").arg(m)); } else m_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, ... // KILE_DEBUG() << "\t\temitting: " << m << endl; if ( fire && !fireSuspended ) emit( foundItem(m, tagLine, tagCol, (*it).type, (*it).level, tagStartLine, tagStartCol, (*it).pix, (*it).folder) ); } //if m } // if tagStart } // while tagStart } //for checkChangedDeps(); emit(doneUpdating()); emit(isrootChanged(isLaTeXRoot())); } void LaTeXInfo::checkChangedDeps() { if( m_depsPrev != m_deps ) { KILE_DEBUG() << "===void LaTeXInfo::checkChangedDeps()===, deps have changed"<< endl; emit(depChanged()); m_depsPrev = m_deps; } } BibInfo::BibInfo (Kate::Document *doc, Extensions *extensions, LatexCommands* /* commands */) : TextInfo(doc, extensions, "BibTeX") { documentTypePromotionAllowed = false; } BibInfo::~BibInfo() { } bool BibInfo::isLaTeXRoot() { return false; } void BibInfo::updateStruct() { if ( getDoc() == 0L ) return; Info::updateStruct(); KILE_DEBUG() << "==void BibInfo::updateStruct()========" << endl; static TQRegExp reItem("^(\\s*)@([a-zA-Z]+)"); static TQRegExp reSpecial("string|preamble|comment"); TQString s, key; int col = 0, startcol, startline = 0; for(uint i = 0; i < m_doc->numLines(); ++i) { s = m_doc->textLine(i); if ( (s.find(reItem) != -1) && !reSpecial.exactMatch(reItem.cap(2).lower()) ) { KILE_DEBUG() << "found: " << reItem.cap(2) << endl; //start looking for key key = ""; bool keystarted = false; int state = 0; startcol = reItem.cap(1).length(); col = startcol + reItem.cap(2).length(); while ( col < static_cast(s.length()) ) { ++col; if ( col == static_cast(s.length()) ) { do { ++i; s = m_doc->textLine(i); } while ( (s.length() == 0) && (i < m_doc->numLines()) ); if ( i == m_doc->numLines() ) break; col = 0; } if ( state == 0 ) { if ( s[col] == '{' ) state = 1; else if ( ! s[col].isSpace() ) break; } else if ( state == 1 ) { if ( s[col] == ',' ) { key = key.stripWhiteSpace(); KILE_DEBUG() << "found: " << key << endl; m_bibItems.append(key); emit(foundItem(key, startline+1, startcol, KileStruct::BibItem, 0, startline+1, startcol, "viewbib", reItem.cap(2).lower()) ); break; } else { key += s[col]; if (!keystarted) { startcol = col; startline = i; } keystarted=true; } } } } } emit(doneUpdating()); } Type BibInfo::getType() { return BibTeX; } TQString BibInfo::getFileFilter() const { return m_extensions->bibtexFileFilter(); } ScriptInfo::ScriptInfo(Kate::Document *doc, Extensions *extensions ) : TextInfo(doc, extensions, "JavaScript") { documentTypePromotionAllowed = false; } ScriptInfo::~ScriptInfo() { } bool ScriptInfo::isLaTeXRoot() { return false; } Type ScriptInfo::getType() { return Script; } TQString ScriptInfo::getFileFilter() const { return m_extensions->scriptFileFilter(); } } #include "kiledocumentinfo.moc"