File indexing completed on 2024-12-01 09:50:25
0001 #include "debugdocument.h" 0002 #include "debugwindow.h" 0003 0004 #include <QApplication> 0005 0006 #include "kjs_binding.h" 0007 #include "khtml_part.h" 0008 0009 #include <ktexteditor/document.h> 0010 #include <ktexteditor/view.h> 0011 #include <ktexteditor/editor.h> 0012 #include <ktexteditor/configinterface.h> 0013 #include <ktexteditor/markinterface.h> 0014 #include <kiconloader.h> 0015 #include <kmessagebox.h> 0016 #include <qcryptographichash.h> 0017 0018 using namespace KJS; 0019 using namespace KJSDebugger; 0020 0021 DebugDocument::DebugDocument(KJS::Interpreter *intp, const QString &url, 0022 int sourceId, int baseLine, const QString &source) 0023 { 0024 m_interpreter = intp; 0025 m_url = url; 0026 0027 m_firstLine = baseLine; 0028 m_sourceId = sourceId; 0029 m_sourceLines = source.split('\n'); 0030 0031 QUrl kurl(url); 0032 m_name = kurl.fileName(); 0033 0034 // Might have to fall back in case of query-like things; 0035 // ad scripts tend to do that 0036 while (m_name.contains("=") || m_name.contains("&")) { 0037 kurl = kurl.upUrl(); 0038 m_name = kurl.fileName(); 0039 } 0040 0041 if (m_name.isEmpty()) { 0042 m_name = kurl.host(); 0043 } 0044 0045 if (m_name.isEmpty()) { 0046 m_name = "????"; //Probably better than un-i18n'd 'undefined'... 0047 } 0048 0049 m_kteDoc = 0; 0050 m_kteView = 0; 0051 m_rebuilding = false; 0052 m_reload = false; 0053 m_hasFunctions = false; 0054 } 0055 0056 DebugDocument::~DebugDocument() 0057 { 0058 emit documentDestroyed(this); 0059 0060 // View has an another parent for UI purposes, so we have to clean it up 0061 delete m_kteView; 0062 } 0063 0064 KJS::Interpreter *DebugDocument::interpreter() 0065 { 0066 return m_interpreter; 0067 } 0068 0069 bool DebugDocument::hasFunctions() 0070 { 0071 return m_hasFunctions; 0072 } 0073 0074 void DebugDocument::setHasFunctions() 0075 { 0076 m_hasFunctions = true; 0077 } 0078 0079 QString DebugDocument::name() const 0080 { 0081 return m_name; 0082 } 0083 0084 int DebugDocument::sid() const 0085 { 0086 return m_sourceId; 0087 } 0088 0089 int DebugDocument::baseLine() const 0090 { 0091 return m_firstLine; 0092 } 0093 0094 int DebugDocument::length() const 0095 { 0096 return m_sourceLines.size(); 0097 } 0098 0099 QString DebugDocument::url() const 0100 { 0101 return m_url; 0102 } 0103 0104 void DebugDocument::reloaded(int sourceId, const QString &source) 0105 { 0106 assert(m_reload); 0107 m_reload = false; 0108 0109 m_sourceLines = source.split('\n'); 0110 m_sourceId = sourceId; 0111 m_md5.clear(); 0112 0113 if (m_kteDoc) { // Update docu if needed 0114 rebuildViewerDocument(); 0115 } 0116 } 0117 0118 void DebugDocument::markReload() 0119 { 0120 m_reload = true; 0121 } 0122 0123 bool DebugDocument::isMarkedReload() const 0124 { 0125 return m_reload; 0126 } 0127 0128 void DebugDocument::setBreakpoint(int lineNumber) 0129 { 0130 if (m_rebuilding) { 0131 return; 0132 } 0133 0134 breakpoints().append(lineNumber); 0135 } 0136 0137 void DebugDocument::removeBreakpoint(int lineNumber) 0138 { 0139 if (m_rebuilding) { 0140 return; 0141 } 0142 0143 QVector<int> &br = breakpoints(); 0144 int idx = breakpoints().indexOf(lineNumber); 0145 if (idx != -1) { 0146 br.remove(idx); 0147 if (br.isEmpty() && !m_url.isEmpty()) { 0148 // We just removed the last breakpoint per URL, 0149 // so we can kill the entire list 0150 s_perUrlBreakPoints->remove(url()); 0151 } 0152 } 0153 } 0154 0155 bool DebugDocument::hasBreakpoint(int lineNumber) 0156 { 0157 return breakpoints().contains(lineNumber); 0158 } 0159 0160 QHash<QString, QVector<int> > *DebugDocument::s_perUrlBreakPoints = 0; 0161 QHash<QString, QVector<int> > *DebugDocument::s_perHashBreakPoints = 0; 0162 0163 QVector<int> &DebugDocument::breakpoints() 0164 { 0165 if (m_url.isEmpty()) { 0166 if (!s_perHashBreakPoints) { 0167 s_perHashBreakPoints = new QHash<QString, QVector<int> >; 0168 } 0169 0170 if (m_md5.isEmpty()) { 0171 QCryptographicHash hash(QCryptographicHash::Md5); 0172 hash.addData(m_sourceLines.join("\n").toUtf8()); 0173 m_md5 = QString::fromLatin1(hash.result().toHex()); 0174 } 0175 0176 return (*s_perHashBreakPoints)[m_md5]; 0177 } else { 0178 if (!s_perUrlBreakPoints) { 0179 s_perUrlBreakPoints = new QHash<QString, QVector<int> >; 0180 } 0181 0182 return (*s_perUrlBreakPoints)[m_url]; 0183 } 0184 } 0185 0186 KTextEditor::Document *DebugDocument::viewerDocument() 0187 { 0188 if (!m_kteDoc) { 0189 rebuildViewerDocument(); 0190 } 0191 return m_kteDoc; 0192 } 0193 0194 KTextEditor::Editor *DebugDocument::s_kate = 0; 0195 0196 KTextEditor::Editor *DebugDocument::kate() 0197 { 0198 if (!s_kate) { 0199 s_kate = KTextEditor::editor("katepart"); 0200 } 0201 0202 if (!s_kate) { 0203 KMessageBox::error(DebugWindow::window(), i18n("Unable to find the Kate editor component;\n" 0204 "please check your KDE installation.")); 0205 qApp->exit(1); 0206 } 0207 0208 return s_kate; 0209 } 0210 0211 void DebugDocument::rebuildViewerDocument() 0212 { 0213 m_rebuilding = true; 0214 0215 if (!m_kteDoc) { 0216 m_kteDoc = kate()->createDocument(this); 0217 setupViewerDocument(); 0218 } 0219 0220 KTextEditor::Cursor oldPos; 0221 if (m_kteView) { 0222 oldPos = m_kteView->cursorPosition(); 0223 } 0224 0225 m_kteDoc->setReadWrite(true); 0226 m_kteDoc->setText(m_sourceLines.join("\n")); 0227 0228 // Restore cursor pos, if there is a view 0229 if (m_kteView) { 0230 m_kteView->setCursorPosition(oldPos); 0231 } 0232 0233 // Check off the pending/URL-based breakpoints. We have to do even 0234 // when the document is being updated as they may be on later lines 0235 // Note that we have to fiddle with them based on our base line, as 0236 // views will always start at 0, even for later fragments 0237 KTextEditor::MarkInterface *imark = qobject_cast<KTextEditor::MarkInterface *>(m_kteDoc); 0238 if (imark) { 0239 QVector<int> &bps = breakpoints(); 0240 foreach (int bpLine, bps) { 0241 int relLine = bpLine - m_firstLine; 0242 if (0 <= relLine && relLine < length()) { 0243 imark->addMark(relLine, KTextEditor::MarkInterface::BreakpointActive); 0244 } 0245 } 0246 } 0247 0248 m_kteDoc->setReadWrite(false); 0249 m_rebuilding = false; 0250 } 0251 0252 void DebugDocument::setupViewerDocument() 0253 { 0254 // Highlight as JS.. 0255 m_kteDoc->setMode("JavaScript"); 0256 0257 // Configure all the breakpoint/execution point marker stuff. 0258 // ### there is an odd split of mark use between here and DebugWindow. 0259 // Perhaps we should just emit a single and let it do it, and 0260 // limit ourselves to ownership? 0261 KTextEditor::MarkInterface *imark = qobject_cast<KTextEditor::MarkInterface *>(m_kteDoc); 0262 assert(imark); 0263 0264 imark->setEditableMarks(KTextEditor::MarkInterface::BreakpointActive); 0265 connect(m_kteDoc, SIGNAL(markChanged(KTextEditor::Document*,KTextEditor::Mark,KTextEditor::MarkInterface::MarkChangeAction)), 0266 DebugWindow::window(), SLOT(markSet(KTextEditor::Document*,KTextEditor::Mark,KTextEditor::MarkInterface::MarkChangeAction))); 0267 0268 imark->setMarkDescription(KTextEditor::MarkInterface::BreakpointActive, 0269 i18n("Breakpoint")); 0270 imark->setMarkPixmap(KTextEditor::MarkInterface::BreakpointActive, 0271 SmallIcon("flag-red")); 0272 imark->setMarkPixmap(KTextEditor::MarkInterface::Execution, 0273 SmallIcon("arrow-right")); 0274 } 0275 0276 KTextEditor::View *DebugDocument::viewerView() 0277 { 0278 if (m_kteView) { 0279 return m_kteView; 0280 } 0281 0282 // Ensure document is created 0283 viewerDocument(); 0284 0285 m_kteView = m_kteDoc->createView(DebugWindow::window()); 0286 KTextEditor::ConfigInterface *iconf = qobject_cast<KTextEditor::ConfigInterface *>(m_kteView); 0287 assert(iconf); 0288 if (iconf->configKeys().contains("line-numbers")) { 0289 iconf->setConfigValue("line-numbers", false); 0290 } 0291 if (iconf->configKeys().contains("icon-bar")) { 0292 iconf->setConfigValue("icon-bar", true); 0293 } 0294 if (iconf->configKeys().contains("dynamic-word-wrap")) { 0295 iconf->setConfigValue("dynamic-word-wrap", true); 0296 } 0297 0298 return m_kteView; 0299 } 0300 0301 #include "moc_debugdocument.cpp"