1 | // SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. |
2 | // |
3 | // SPDX-License-Identifier: GPL-3.0-or-later |
4 | |
5 | #include "style/lspclientkeeper.h" |
6 | #include "textedittabwidget.h" |
7 | #include "textedittitlebar.h" |
8 | #include "textedittabbar.h" |
9 | #include "textedit.h" |
10 | #include "mainframe/texteditkeeper.h" |
11 | #include "transceiver/codeeditorreceiver.h" |
12 | #include "codelens/codelens.h" |
13 | #include "common/common.h" |
14 | #include <QGridLayout> |
15 | #include <QFileInfo> |
16 | #include <QKeyEvent> |
17 | |
18 | static TextEditTabWidget *ins{nullptr}; |
19 | |
20 | class TextEditTabWidgetPrivate |
21 | { |
22 | friend class TextEditTabWidget; |
23 | TextEditTabBar *tab {nullptr}; |
24 | QGridLayout *gridLayout {nullptr}; |
25 | QHash<QString, TextEdit*> textEdits{}; |
26 | QHash<QString, TextEditTitleBar*> titleBars{}; |
27 | QHash<QString, bool> textEditAutoReloadFlags; |
28 | TextEdit defaultEdit; |
29 | QString runningFilePathCache; |
30 | bool selFlag = false; |
31 | QColor selColor{}; |
32 | QColor defColor{}; |
33 | }; |
34 | |
35 | TextEditTabWidget::TextEditTabWidget(QWidget *parent) |
36 | : QWidget(parent) |
37 | , d(new TextEditTabWidgetPrivate) |
38 | { |
39 | d->gridLayout = new QGridLayout(this); |
40 | d->gridLayout->setSpacing(0); |
41 | d->gridLayout->setMargin(0); |
42 | |
43 | d->tab = new TextEditTabBar(this); |
44 | d->gridLayout->addWidget(d->tab); |
45 | d->gridLayout->addWidget(&d->defaultEdit); |
46 | this->setLayout(d->gridLayout); |
47 | this->setAcceptDrops(true); |
48 | |
49 | setDefaultFileEdit(); |
50 | setFocusPolicy(Qt::ClickFocus); |
51 | |
52 | QObject::connect(EditorCallProxy::instance(), &EditorCallProxy::toOpenFileWithKey, |
53 | this, &TextEditTabWidget::openFileWithKey); |
54 | |
55 | QObject::connect(EditorCallProxy::instance(), &EditorCallProxy::toRunFileLineWithKey, |
56 | this, &TextEditTabWidget::runningToLineWithKey); |
57 | |
58 | QObject::connect(EditorCallProxy::instance(), &EditorCallProxy::toRunClean, |
59 | this, &TextEditTabWidget::runningEnd); |
60 | |
61 | QObject::connect(EditorCallProxy::instance(), &EditorCallProxy::toDebugPointClean, |
62 | this, &TextEditTabWidget::debugPointClean); |
63 | |
64 | QObject::connect(EditorCallProxy::instance(), &EditorCallProxy::toJumpFileLineWithKey, |
65 | this, &TextEditTabWidget::jumpToLineWithKey); |
66 | |
67 | QObject::connect(EditorCallProxy::instance(), &EditorCallProxy::toSetLineBackground, |
68 | this, &TextEditTabWidget::setLineBackground); |
69 | |
70 | QObject::connect(EditorCallProxy::instance(), &EditorCallProxy::toDelLineBackground, |
71 | this, &TextEditTabWidget::delLineBackground); |
72 | |
73 | QObject::connect(EditorCallProxy::instance(), &EditorCallProxy::toCleanLineBackground, |
74 | this, &TextEditTabWidget::cleanLineBackground); |
75 | |
76 | QObject::connect(EditorCallProxy::instance(), &EditorCallProxy::toSetAnnotation, |
77 | this, &TextEditTabWidget::setAnnotation); |
78 | |
79 | QObject::connect(EditorCallProxy::instance(), &EditorCallProxy::toCleanAnnotation, |
80 | this, &TextEditTabWidget::cleanAnnotation); |
81 | |
82 | QObject::connect(EditorCallProxy::instance(), &EditorCallProxy::toCleanAllAnnotation, |
83 | this, &TextEditTabWidget::cleanAllAnnotation); |
84 | |
85 | QObject::connect(d->tab, &TextEditTabBar::fileSwitched, |
86 | this, &TextEditTabWidget::showFileEdit, Qt::QueuedConnection); |
87 | |
88 | QObject::connect(d->tab, &TextEditTabBar::fileClosed, |
89 | this, &TextEditTabWidget::removeFileEdit, Qt::QueuedConnection); |
90 | |
91 | QObject::connect(d->tab, &TextEditTabBar::fileClosed, |
92 | this, &TextEditTabWidget::removeFileStatusBar, Qt::QueuedConnection); |
93 | |
94 | QObject::connect(d->tab, &TextEditTabBar::closeClicked, this, &TextEditTabWidget::closed); |
95 | |
96 | QObject::connect(d->tab, &TextEditTabBar::splitClicked, this, [=](Qt::Orientation ori) { |
97 | QString currSelFile = d->tab->currentFile(); |
98 | newlsp::ProjectKey key = {}; |
99 | if(d->textEdits[currSelFile] && !currSelFile.isEmpty()) { |
100 | key = d->textEdits[currSelFile]->projectKey(); |
101 | } |
102 | emit splitClicked(ori, key, currSelFile); |
103 | }); |
104 | |
105 | QObject::connect(Inotify::globalInstance(), &Inotify::deletedSelf, |
106 | this, &TextEditTabWidget::fileDeleted, Qt::QueuedConnection); |
107 | QObject::connect(Inotify::globalInstance(), &Inotify::movedSelf, |
108 | this, &TextEditTabWidget::fileMoved, Qt::QueuedConnection); |
109 | QObject::connect(Inotify::globalInstance(), &Inotify::modified, |
110 | this, &TextEditTabWidget::fileModifyed, Qt::QueuedConnection); |
111 | } |
112 | |
113 | TextEditTabWidget::~TextEditTabWidget() |
114 | { |
115 | if (d) { |
116 | if (d->tab) { |
117 | delete d->tab; |
118 | d->tab = nullptr; |
119 | } |
120 | |
121 | auto editsItera = d->textEdits.begin(); |
122 | while (editsItera != d->textEdits.end()) { |
123 | delete editsItera.value(); // free instance |
124 | editsItera = d->textEdits.erase(editsItera); |
125 | } |
126 | |
127 | auto titleItera = d->titleBars.begin(); |
128 | while (titleItera != d->titleBars.end()) { |
129 | delete titleItera.value(); |
130 | titleItera = d->titleBars.erase(titleItera); |
131 | } |
132 | |
133 | delete d; // free private |
134 | } |
135 | } |
136 | |
137 | TextEditTabWidget *TextEditTabWidget::instance() |
138 | { |
139 | if (!ins) |
140 | ins = new TextEditTabWidget; |
141 | return ins; |
142 | } |
143 | |
144 | void TextEditTabWidget::openFile(const QString &filePath) |
145 | { |
146 | QFileInfo info(filePath); |
147 | if (!info.exists() || !d->tab ) |
148 | return; |
149 | // can't add widget to much |
150 | if (d->textEdits.keys().contains(info.filePath())) { |
151 | d->tab->switchFile(filePath); |
152 | return; |
153 | } |
154 | |
155 | d->tab->setFile(filePath); |
156 | |
157 | |
158 | TextEdit *edit = new TextEdit; |
159 | |
160 | QObject::connect(edit, &TextEdit::focusChanged, |
161 | this, &TextEditTabWidget::selectSelf); |
162 | |
163 | QObject::connect(edit, &TextEdit::fileChanged, d->tab, |
164 | &TextEditTabBar::doFileChanged, Qt::UniqueConnection); |
165 | |
166 | QObject::connect(edit, &TextEdit::fileSaved, d->tab, |
167 | &TextEditTabBar::doFileSaved, Qt::UniqueConnection); |
168 | |
169 | edit->setFile(info.filePath()); |
170 | d->textEdits[filePath] = edit; |
171 | |
172 | // 添加监听 |
173 | Inotify::globalInstance()->addPath(info.filePath()); |
174 | // set display textedit |
175 | d->gridLayout->addWidget(edit); |
176 | |
177 | if (!d->defaultEdit.isHidden()) |
178 | d->defaultEdit.hide(); |
179 | |
180 | d->tab->switchFile(filePath); |
181 | |
182 | d->textEditAutoReloadFlags[filePath] = false; |
183 | |
184 | showFileEdit(filePath); |
185 | emit sigOpenFile(); |
186 | } |
187 | |
188 | void TextEditTabWidget::openFileWithKey(const newlsp::ProjectKey &key, const QString &filePath) |
189 | { |
190 | QFileInfo info(filePath); |
191 | if (!info.exists() || !d->tab ) |
192 | return; |
193 | // can't add widget to much |
194 | if (d->textEdits.keys().contains(info.filePath())) { |
195 | d->tab->switchFile(filePath); |
196 | return; |
197 | } |
198 | |
199 | d->tab->setFile(filePath); |
200 | |
201 | newlsp::Client *client = LSPClientKeeper::instance()->get(key); |
202 | if (!client) { |
203 | client = LSPClientKeeper::instance()->get(key); |
204 | } |
205 | |
206 | // 全局rename操作 |
207 | QObject::connect(client, QOverload<const newlsp::WorkspaceEdit&>::of(&newlsp::Client::renameRes), |
208 | this, &TextEditTabWidget::doRenameReplace, Qt::UniqueConnection); |
209 | |
210 | // 使用取出适用的编辑器 |
211 | TextEdit *edit = TextEditKeeper::create(TextEdit::fileLanguage(filePath)); |
212 | |
213 | QObject::connect(edit, &TextEdit::fileChanged, d->tab, |
214 | &TextEditTabBar::doFileChanged, Qt::UniqueConnection); |
215 | |
216 | QObject::connect(edit, &TextEdit::fileSaved, d->tab, |
217 | &TextEditTabBar::doFileSaved, Qt::UniqueConnection); |
218 | |
219 | QObject::connect(d->tab, &TextEditTabBar::saveFile, |
220 | this, &TextEditTabWidget::saveEditFile, |
221 | Qt::UniqueConnection); |
222 | |
223 | if (edit){ |
224 | edit->setProjectKey(key); |
225 | edit->setFile(info.filePath()); |
226 | } else { |
227 | edit = new TextEdit(); |
228 | edit->setProjectKey(key); |
229 | edit->setFile(info.filePath()); |
230 | } |
231 | |
232 | QObject::connect(edit, &TextEdit::focusChanged, |
233 | this, &TextEditTabWidget::selectSelf); |
234 | |
235 | d->textEdits[filePath] = edit; |
236 | // 添加监听 |
237 | Inotify::globalInstance()->addPath(info.filePath()); |
238 | // set display textedit |
239 | d->gridLayout->addWidget(edit); |
240 | |
241 | if (!d->defaultEdit.isHidden()) |
242 | d->defaultEdit.hide(); |
243 | |
244 | d->tab->switchFile(filePath); |
245 | |
246 | d->textEditAutoReloadFlags[filePath] = false; |
247 | |
248 | showFileEdit(filePath); |
249 | emit sigOpenFile(); |
250 | } |
251 | |
252 | void TextEditTabWidget::closeFile(const QString &filePath) |
253 | { |
254 | Inotify::globalInstance()->removePath(filePath); |
255 | |
256 | if (!d->tab) |
257 | return; |
258 | |
259 | int index = d->tab->fileIndex(filePath); |
260 | if (index >=0 && index < d->tab->count()) |
261 | emit d->tab->tabCloseRequested(index); |
262 | } |
263 | |
264 | void TextEditTabWidget::jumpToLineWithKey(const newlsp::ProjectKey &key, const QString &filePath, int line) |
265 | { |
266 | auto edit = switchFileAndToOpen(key, filePath); |
267 | |
268 | if (edit) { |
269 | edit->jumpToLine(line); |
270 | } |
271 | } |
272 | |
273 | void TextEditTabWidget::jumpToLine(const QString &filePath, int line) |
274 | { |
275 | auto edit = switchFileAndToOpen(filePath); |
276 | |
277 | if (edit) { |
278 | edit->jumpToLine(line); |
279 | } |
280 | } |
281 | |
282 | void TextEditTabWidget::jumpToRange(const QString &filePath, const newlsp::Range &range) |
283 | { |
284 | auto edit = switchFileAndToOpen(filePath); |
285 | |
286 | if (edit) { |
287 | auto styleLsp = edit->getStyleLsp(); |
288 | if (styleLsp) { |
289 | auto start = styleLsp->getSciPosition(edit->docPointer(), range.start); |
290 | auto end = styleLsp->getSciPosition(edit->docPointer(), range.end); |
291 | edit->jumpToRange(start, end); |
292 | } |
293 | } |
294 | } |
295 | |
296 | void TextEditTabWidget::runningToLineWithKey(const newlsp::ProjectKey &key, const QString &filePath, int line) |
297 | { |
298 | auto edit = switchFileAndToOpen(key, filePath); |
299 | |
300 | if (edit) { |
301 | edit->jumpToLine(line); |
302 | edit->runningToLine(line); |
303 | } |
304 | } |
305 | |
306 | void TextEditTabWidget::runningToLine(const QString &filePath, int line) |
307 | { |
308 | auto edit = switchFileAndToOpen(filePath); |
309 | |
310 | if (edit) { |
311 | edit->jumpToLine(line); |
312 | edit->runningToLine(line); |
313 | } |
314 | } |
315 | |
316 | void TextEditTabWidget::runningEnd() |
317 | { |
318 | for (auto edit : d->textEdits) { |
319 | edit->runningEnd(); |
320 | } |
321 | } |
322 | |
323 | void TextEditTabWidget::addDebugPoint(const QString &filePath, int line) |
324 | { |
325 | for (auto edit : d->textEdits) { |
326 | if (filePath == edit->file()) { |
327 | edit->addDebugPoint(line); |
328 | } |
329 | } |
330 | } |
331 | |
332 | void TextEditTabWidget::removeDebugPoint(const QString &filePath, int line) |
333 | { |
334 | for (auto edit : d->textEdits) { |
335 | if (filePath == edit->file()) { |
336 | edit->removeDebugPoint(line); |
337 | } |
338 | } |
339 | } |
340 | |
341 | void TextEditTabWidget::debugPointClean() |
342 | { |
343 | for (auto edit : d->textEdits) { |
344 | edit->debugPointAllDelete(); |
345 | } |
346 | } |
347 | |
348 | void TextEditTabWidget::replaceRange(const QString &filePath, const newlsp::Range &range, const QString &text) |
349 | { |
350 | auto edit = d->textEdits.value(filePath); |
351 | if (edit) { |
352 | auto styleLsp = edit->getStyleLsp(); |
353 | if (styleLsp) { |
354 | auto start = styleLsp->getSciPosition(edit->docPointer(), range.start); |
355 | auto end = styleLsp->getSciPosition(edit->docPointer(), range.end); |
356 | edit->replaceRange(start, end, text); |
357 | } |
358 | } else { //直接更改磁盘数据 |
359 | if (range.start.line != range.end.line) { |
360 | qCritical() << "Failed, Unknown error" ; |
361 | abort(); |
362 | } |
363 | QFile changeFile(filePath); |
364 | QString cacheData; |
365 | if (changeFile.open(QFile::ReadOnly)) { |
366 | int i = 0; |
367 | while (i != range.start.line) { |
368 | cacheData += changeFile.readLine(); |
369 | i++; |
370 | } |
371 | QString changeLine = changeFile.readLine(); |
372 | int removeLength = range.end.character - range.start.character; |
373 | changeLine = changeLine.replace(range.start.character, removeLength, text); |
374 | cacheData += changeLine; |
375 | QByteArray array = changeFile.readLine(); |
376 | while (!array.isEmpty()) { |
377 | cacheData += array; |
378 | array = changeFile.readLine(); |
379 | } |
380 | changeFile.close(); |
381 | } |
382 | |
383 | if (changeFile.open(QFile::WriteOnly | QFile::Truncate)) { |
384 | int writeCount = changeFile.write(cacheData.toLatin1()); |
385 | if (writeCount != cacheData.size()) { |
386 | qCritical() << "Failed, Unknown error" ; |
387 | abort(); |
388 | } |
389 | changeFile.close(); |
390 | } |
391 | } |
392 | } |
393 | |
394 | void TextEditTabWidget::setLineBackground(const QString &filePath, int line, const QColor &color) |
395 | { |
396 | if (!d->gridLayout) |
397 | return; |
398 | |
399 | auto edit = d->textEdits.value(filePath); |
400 | |
401 | if (!edit) |
402 | return; |
403 | |
404 | edit->setLineBackground(line, color); |
405 | } |
406 | |
407 | void TextEditTabWidget::delLineBackground(const QString &filePath, int line) |
408 | { |
409 | if (!d->gridLayout) |
410 | return; |
411 | |
412 | auto edit = d->textEdits.value(filePath); |
413 | |
414 | if (!edit) |
415 | return; |
416 | |
417 | edit->delLineBackground(line); |
418 | } |
419 | |
420 | void TextEditTabWidget::cleanLineBackground(const QString &filePath) |
421 | { |
422 | if (!d->gridLayout) |
423 | return; |
424 | |
425 | auto edit = d->textEdits.value(filePath); |
426 | |
427 | if (!edit) |
428 | return; |
429 | |
430 | edit->cleanLineBackground(); |
431 | } |
432 | |
433 | void TextEditTabWidget::setAnnotation(const QString &filePath, int line, |
434 | const QString &title, const AnnotationInfo &info) |
435 | { |
436 | if (!d->gridLayout) |
437 | return; |
438 | |
439 | auto edit = d->textEdits.value(filePath); |
440 | |
441 | if (!edit) |
442 | return; |
443 | |
444 | edit->setAnnotation(line, title, info); |
445 | } |
446 | |
447 | void TextEditTabWidget::cleanAnnotation(const QString &filePath, const QString &title) |
448 | { |
449 | if (!d->gridLayout) |
450 | return; |
451 | |
452 | auto edit = d->textEdits.value(filePath); |
453 | |
454 | if (!edit) |
455 | return; |
456 | |
457 | edit->cleanAnnotation(title); |
458 | } |
459 | |
460 | void TextEditTabWidget::cleanAllAnnotation(const QString &title) |
461 | { |
462 | for (auto filePath : d->textEdits.keys()) { |
463 | cleanAnnotation(filePath, title); |
464 | } |
465 | } |
466 | |
467 | void TextEditTabWidget::selectSelf(bool state) |
468 | { |
469 | d->selFlag = state; |
470 | emit selected(state); |
471 | update(); |
472 | } |
473 | |
474 | void TextEditTabWidget::setModifiedAutoReload(const QString &filePath, bool flag) |
475 | { |
476 | d->textEditAutoReloadFlags[filePath] = flag; |
477 | } |
478 | |
479 | void TextEditTabWidget::setDefaultFileEdit() |
480 | { |
481 | if (!d || !d->gridLayout) |
482 | return; |
483 | |
484 | d->gridLayout->addWidget(&d->defaultEdit); |
485 | d->defaultEdit.setEnabled(false); |
486 | d->defaultEdit.show(); |
487 | } |
488 | |
489 | void TextEditTabWidget::hideFileEdit(const QString &file) |
490 | { |
491 | if (!d->gridLayout) |
492 | return; |
493 | |
494 | auto edit = d->textEdits.value(file); |
495 | |
496 | if (!edit) |
497 | return; |
498 | |
499 | edit->hide(); |
500 | } |
501 | |
502 | void TextEditTabWidget::showFileEdit(const QString &file) |
503 | { |
504 | if (!d->gridLayout) |
505 | return; |
506 | |
507 | auto edit = d->textEdits.value(file); |
508 | |
509 | if (!edit) |
510 | return; |
511 | |
512 | auto itera = d->textEdits.begin(); |
513 | while (itera != d->textEdits.end()){ |
514 | if (itera.key() != file && itera.value()){ |
515 | itera.value()->hide(); // hide other; |
516 | } |
517 | itera ++; |
518 | } |
519 | |
520 | edit->show(); |
521 | } |
522 | |
523 | void TextEditTabWidget::hideFileStatusBar(const QString &file) |
524 | { |
525 | auto statusBar = d->titleBars.value(file); |
526 | statusBar->hide(); |
527 | } |
528 | |
529 | void TextEditTabWidget::showFileStatusBar(const QString &file) |
530 | { |
531 | if (d->titleBars.contains(file)) { |
532 | auto statusBar = d->titleBars.value(file); |
533 | if (statusBar) |
534 | statusBar->show(); |
535 | } |
536 | } |
537 | |
538 | void TextEditTabWidget::removeFileStatusBar(const QString &file) |
539 | { |
540 | if (d->textEditAutoReloadFlags.contains(file)) |
541 | d->textEditAutoReloadFlags.remove(file); |
542 | |
543 | auto statusBar = d->titleBars.value(file); |
544 | if (!statusBar) |
545 | return; |
546 | |
547 | delete statusBar; |
548 | d->titleBars.remove(file); |
549 | } |
550 | |
551 | void TextEditTabWidget::removeFileEdit(const QString &file) |
552 | { |
553 | auto edit = d->textEdits.value(file); |
554 | if (!edit) |
555 | return; |
556 | |
557 | edit->fileClosed(file); |
558 | edit->deleteLater(); |
559 | d->textEdits.remove(file); |
560 | |
561 | if (d->textEdits.size() == 0) { |
562 | setDefaultFileEdit(); |
563 | emit closeWidget(); |
564 | } |
565 | |
566 | } |
567 | |
568 | void TextEditTabWidget::removeFileTab(const QString &file) |
569 | { |
570 | d->tab->removeTab(file); |
571 | } |
572 | |
573 | void TextEditTabWidget::fileModifyed(const QString &file) |
574 | { |
575 | auto edit = d->textEdits[file]; |
576 | if (edit && !edit->isHidden() && !edit->isSaveText()) { |
577 | |
578 | if (!d->titleBars[file]) { |
579 | d->titleBars[file] = TextEditTitleBar::changedReload(file); |
580 | QObject::connect(d->titleBars[file], &TextEditTitleBar::reloadfile, [=](){ |
581 | if(d->titleBars.contains(file) && d->textEdits.contains(file)) { |
582 | d->textEdits[file]->updateFile(); |
583 | } |
584 | }); |
585 | } |
586 | |
587 | if (d->textEditAutoReloadFlags.contains(file) && d->textEditAutoReloadFlags[file]) { |
588 | d->titleBars[file]->reloadfile(); |
589 | } else { |
590 | d->gridLayout->addWidget(d->titleBars[file], 1, 0); |
591 | d->titleBars[file]->show(); |
592 | } |
593 | } |
594 | |
595 | // 100 ms 内多次出发变动将忽略 |
596 | QTimer::singleShot(100, [=](){edit->cleanIsSaveText();}); |
597 | } |
598 | |
599 | void TextEditTabWidget::fileDeleted(const QString &file) |
600 | { |
601 | detectFile(file); |
602 | } |
603 | |
604 | void TextEditTabWidget::fileMoved(const QString &file) |
605 | { |
606 | detectFile(file); |
607 | } |
608 | |
609 | void TextEditTabWidget::detectFile(const QString &file) |
610 | { |
611 | QFileInfo info(file); |
612 | if (info.exists()) { |
613 | fileModifyed(file); |
614 | Inotify::globalInstance()->addPath(file); |
615 | } else { |
616 | Inotify::globalInstance()->removePath(file); |
617 | handleDeletedFile(file); |
618 | } |
619 | } |
620 | |
621 | void TextEditTabWidget::setCloseButtonVisible(bool flag) |
622 | { |
623 | d->tab->setCloseButtonVisible(flag); |
624 | } |
625 | |
626 | void TextEditTabWidget::setSplitButtonVisible(bool flag) |
627 | { |
628 | d->tab->setSplitButtonVisible(flag); |
629 | } |
630 | |
631 | void TextEditTabWidget::handleDeletedFile(const QString &file) |
632 | { |
633 | int ret = QMessageBox::question(this, QMessageBox::tr("File Has Been Removed" ), |
634 | QMessageBox::tr("The file has been removed, Do you want to save it?" ), |
635 | QMessageBox::Save | QMessageBox::Discard, QMessageBox::Discard); |
636 | if (QMessageBox::Save == ret) { |
637 | TextEdit* edit = d->textEdits.value(file); |
638 | if (edit) { |
639 | edit->saveAsText(); |
640 | Inotify::globalInstance()->addPath(file); |
641 | } |
642 | } else { |
643 | closeFile(file); |
644 | } |
645 | } |
646 | |
647 | void TextEditTabWidget::doRenameReplace(const newlsp::WorkspaceEdit &renameResult) |
648 | { |
649 | if (renameResult.changes) { |
650 | auto changes = renameResult.changes; |
651 | auto itera = changes->begin(); |
652 | while (itera != changes->end()) { |
653 | for (auto edit : itera->second) { |
654 | QString filePath = QUrl(QString::fromStdString(itera->first)).toLocalFile(); |
655 | QString newText = QString::fromStdString(edit.newText); |
656 | replaceRange(filePath, edit.range, newText); |
657 | } |
658 | itera ++; |
659 | } |
660 | } |
661 | if (renameResult.documentChanges) { |
662 | if (newlsp::any_contrast<std::vector<newlsp::TextDocumentEdit>>(renameResult.documentChanges.value())) { |
663 | std::vector<newlsp::TextDocumentEdit> documentChanges |
664 | = std::any_cast<std::vector<newlsp::TextDocumentEdit>>(renameResult.documentChanges.value()); |
665 | for (auto documentChange : documentChanges) { |
666 | QString filePath = QUrl(QString::fromStdString(documentChange.textDocument.uri)).toLocalFile(); |
667 | if (!std::vector<newlsp::TextEdit>(documentChange.edits).empty()) { |
668 | auto edits = std::vector<newlsp::TextEdit>(documentChange.edits); |
669 | for (auto edit : edits) { |
670 | QString newText = QString::fromStdString(edit.newText); |
671 | replaceRange(filePath, edit.range, newText); |
672 | } |
673 | } else if (!std::vector<newlsp::AnnotatedTextEdit>(documentChange.edits).empty()) { |
674 | auto edits = std::vector<newlsp::AnnotatedTextEdit>(documentChange.edits); |
675 | for (auto edit : edits) { |
676 | QString newText = QString::fromStdString(edit.newText); |
677 | replaceRange(filePath, edit.range, newText); |
678 | } |
679 | } |
680 | } |
681 | } |
682 | } |
683 | } |
684 | |
685 | TextEdit* TextEditTabWidget::switchFileAndToOpen(const newlsp::ProjectKey &key, const QString &filePath) |
686 | { |
687 | auto edit = d->textEdits.value(filePath); |
688 | if (edit) { |
689 | d->tab->switchFile(filePath); |
690 | showFileEdit(filePath); |
691 | } else { |
692 | openFileWithKey(key, filePath); |
693 | for (auto textEdit : d->textEdits.values()) { |
694 | textEdit->runningEnd(); |
695 | if (textEdit->file() == filePath) { |
696 | showFileEdit(filePath); |
697 | edit = textEdit; |
698 | } |
699 | } |
700 | } |
701 | return edit; |
702 | } |
703 | |
704 | TextEdit* TextEditTabWidget::switchFileAndToOpen(const QString &filePath) |
705 | { |
706 | auto edit = d->textEdits.value(filePath); |
707 | if (edit) { |
708 | d->tab->switchFile(filePath); |
709 | showFileEdit(filePath); |
710 | } else { |
711 | openFile(filePath); |
712 | for (auto textEdit : d->textEdits.values()) { |
713 | textEdit->runningEnd(); |
714 | if (textEdit->file() == filePath) { |
715 | showFileEdit(filePath); |
716 | edit = textEdit; |
717 | } |
718 | } |
719 | } |
720 | |
721 | showFileStatusBar(filePath); |
722 | return edit; |
723 | } |
724 | |
725 | void TextEditTabWidget::saveEditFile(const QString &file) |
726 | { |
727 | TextEdit* edit = d->textEdits.value(file); |
728 | if (edit) { |
729 | edit->saveText(); |
730 | } |
731 | } |
732 | |
733 | void TextEditTabWidget::keyPressEvent(QKeyEvent *event) |
734 | { |
735 | if (event->modifiers() == Qt::AltModifier) { |
736 | int idx = d->tab->currentIndex(); |
737 | int count = d->tab->count(); |
738 | if (count > 0 && idx > -1) { |
739 | if (event->key() == Qt::Key_Left) { |
740 | d->tab->setCurrentIndex((idx - 1 + count) % count); |
741 | setFocus(); |
742 | } else if (event->key() == Qt::Key_Right) { |
743 | d->tab->setCurrentIndex((idx + 1) % count); |
744 | setFocus(); |
745 | } |
746 | } |
747 | } |
748 | return QWidget::keyPressEvent(event); |
749 | } |
750 | |
751 | void TextEditTabWidget::focusInEvent(QFocusEvent *event) |
752 | { |
753 | QWidget::focusInEvent(event); |
754 | this->selectSelf(true); |
755 | } |
756 | |
757 | void TextEditTabWidget::focusOutEvent(QFocusEvent *event) |
758 | { |
759 | QWidget::focusOutEvent(event); |
760 | this->selectSelf(false); |
761 | } |
762 | |
763 | void TextEditTabWidget::paintEvent(QPaintEvent *event) |
764 | { |
765 | if (d->selFlag) { |
766 | QPainter painter(this); |
767 | painter.save(); |
768 | painter.setPen(d->selColor); |
769 | painter.drawRect(this->rect()); |
770 | painter.restore(); |
771 | } else { |
772 | if (!d->defColor.isValid()) { |
773 | d->defColor = palette().window().color(); |
774 | d->selColor = QColor(d->defColor.red() + 20, d->defColor.green() + 20, d->defColor.blue() + 20, d->defColor.alpha()); |
775 | } else { |
776 | QPainter painter(this); |
777 | painter.save(); |
778 | painter.setPen(d->defColor); |
779 | painter.drawRect(this->rect()); |
780 | painter.restore(); |
781 | } |
782 | } |
783 | } |
784 | |
785 | void TextEditTabWidget::dragEnterEvent(QDragEnterEvent *event) |
786 | { |
787 | if (event->mimeData()->hasUrls()) { |
788 | event->acceptProposedAction(); |
789 | } else { |
790 | event->ignore(); |
791 | } |
792 | } |
793 | |
794 | void TextEditTabWidget::dropEvent(QDropEvent *event) |
795 | { |
796 | const QMimeData* mimeData = event->mimeData(); |
797 | if(mimeData->hasUrls()) { |
798 | QList<QUrl>urlList = mimeData->urls(); |
799 | QString fileName = urlList.at(0).toLocalFile(); |
800 | if(!fileName.isEmpty()) { |
801 | editor.openFile(fileName); |
802 | } |
803 | } |
804 | } |
805 | |