1// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "stylelsp.h"
6#include "Scintilla.h"
7#include "stylesci.h"
8#include "stylecolor.h"
9#include "mainframe/texteditkeeper.h"
10#include "textedittabwidget/textedit.h"
11#include "textedittabwidget/textedittabwidget.h"
12#include "textedittabwidget/style/lspclientkeeper.h"
13#include "codelens/codelens.h"
14#include "transceiver/codeeditorreceiver.h"
15#include "renamepopup/renamepopup.h"
16#include "base/abstractaction.h"
17
18#include "common/common.h"
19#include "framework/service/qtclassmanager.h"
20#include "services/window/windowservice.h"
21
22#include "Document.h"
23
24#include <QHash>
25#include <QTimer>
26#include <QMutex>
27#include <QMutexLocker>
28#include <QMenu>
29#include <QLineEdit>
30#include <QVBoxLayout>
31#include <QCoreApplication>
32#include <QByteArray>
33#include <QShortcut>
34
35#include <bitset>
36
37using namespace dpfservice;
38
39class SciRangeCache
40{
41 std::optional<Scintilla::Position> start;
42 std::optional<Scintilla::Position> end;
43public:
44 SciRangeCache(Scintilla::Position start, Scintilla::Position end)
45 :start(start), end(end){}
46 SciRangeCache(){}
47 void clean() { start.reset(); end.reset(); }
48 bool isEmpty() { return start && end;}
49 Scintilla::Position getStart() const {return start.value();}
50 void setStart(const Scintilla::Position &value) {start = value;}
51 Scintilla::Position getEnd() const {return end.value();}
52 void setEnd(const Scintilla::Position &value) {end = value;}
53 bool operator == (const SciRangeCache &other){
54 return start == other.start && end == other.end;
55 }
56};
57
58class SciPositionCache
59{
60 Scintilla::Position sciPosition = -1;
61public:
62 SciPositionCache(){}
63 SciPositionCache(const Scintilla::Position &pos)
64 : sciPosition(pos){}
65 void clean() {sciPosition = -1;}
66 bool isEmpty() { return sciPosition == -1;}
67 Scintilla::Position getSciPosition() const { return sciPosition;}
68 void setSciPosition(const Scintilla::Position &value) { sciPosition = value;}
69};
70
71class DefinitionCache : public SciPositionCache
72{
73 std::optional<std::vector<newlsp::Location>> locations{};
74 std::optional<std::vector<newlsp::LocationLink>> locationLinks{};
75 std::optional<newlsp::Location> location{};
76 SciRangeCache textRange{};
77 int cursor = 0; //Invalid
78public:
79 void clean() {
80 cleanFromLsp();
81 cursor = 0;
82 SciPositionCache::clean();
83 textRange.clean();
84 }
85 void cleanFromLsp() {
86 if (locations) location.reset();
87 if (location) location.reset();
88 if (locationLinks) locationLinks.reset();
89 }
90
91 bool isEmpty() {
92 return locations && location && locationLinks
93 && cursor == 0 && SciPositionCache::isEmpty()
94 && textRange.isEmpty();
95 }
96 std::vector<newlsp::Location> getLocations() const
97 {
98 if (locations.has_value())
99 return locations.value();
100 return {};
101 }
102 newlsp::Location getLocation() const {return location.value();}
103 std::vector<newlsp::LocationLink> getLocationLinks() const {return locationLinks.value();}
104 void set(const std::vector<newlsp::Location> &value) {locations = value;}
105 void set(const newlsp::Location &value) {location = value;}
106 void set(const std::vector<newlsp::LocationLink> &value) {locationLinks = value;}
107 int getCursor() const {return cursor;}
108 void setCursor(int value) {cursor = value;}
109 SciRangeCache getTextRange() const {return textRange;}
110 void setTextRange(const SciRangeCache &value) {textRange = value;}
111};
112
113class HoverCache : public SciPositionCache
114{
115public:
116 void clean() { SciPositionCache::clean(); }
117 bool isEmpty() { return SciPositionCache::isEmpty(); }
118};
119
120class RenameCache
121{
122 SciPositionCache start;
123 SciPositionCache end;
124public:
125 void clean() { start.clean(); end.clean(); }
126 bool isEmpty() { return start.isEmpty() && end.isEmpty(); }
127 SciPositionCache getStart() const { return start; }
128 void setStart(const SciPositionCache &value) {start = value;}
129 SciPositionCache getEnd() const {return end;}
130 void setEnd(const SciPositionCache &value) {end = value;}
131};
132
133class CompletionCache
134{
135public:
136 lsp::CompletionProvider provider;
137};
138
139struct TextChangeCache
140{
141 enum State{
142 Inserted,
143 Deleted
144 };
145 State state;
146 Scintilla::Position positionCache;
147 Scintilla::Position lengthCache;
148 Scintilla::Position linesAddedCache;
149 QByteArray textCache;
150 Scintilla::Position lineCache;
151};
152
153class StyleLspPrivate
154{
155 CompletionCache completionCache;
156 DefinitionCache definitionCache;
157 QTimer textChangedTimer;
158 HoverCache hoverCache;
159 RenamePopup renamePopup;
160 RenameCache renameCache;
161 TextEdit *edit{nullptr};
162 TextChangeCache textChangedCache;
163 QList<lsp::Data> tokensCache;
164 static QAction *rangeFormattingAction;
165 static QString formattingFile;
166 friend class StyleLsp;
167 newlsp::Client *getClient() const;
168};
169
170QAction *StyleLspPrivate::rangeFormattingAction = nullptr;
171QString StyleLspPrivate::formattingFile = "";
172// from ascii code
173inline bool StyleLsp::isCharSymbol(const char ch) {
174 return (ch >= 0x21 && ch < 0x2F + 1) || (ch >= 0x3A && ch < 0x40 + 1)
175 || (ch >= 0x5B && ch < 0x60 + 1) || (ch >= 0x7B && ch < 0x7E + 1);
176}
177
178Sci_Position StyleLsp::getSciPosition(sptr_t doc, const newlsp::Position &pos)
179{
180 auto docTemp = (Scintilla::Internal::Document*)(doc);
181 return docTemp->GetRelativePosition(docTemp->LineStart(pos.line), pos.character);
182}
183
184lsp::Position StyleLsp::getLspPosition(sptr_t doc, sptr_t sciPosition)
185{
186 auto docTemp = (Scintilla::Internal::Document*)(doc);
187 int line = docTemp->LineFromPosition(sciPosition);
188 Sci_Position lineChStartPos = getSciPosition(doc, newlsp::Position{line, 0});
189 return lsp::Position{line, (int)(sciPosition - lineChStartPos)};
190}
191
192int editLineNumber(int originLineNumber)
193{
194 return originLineNumber + 1;
195}
196
197StyleLsp::StyleLsp(TextEdit *parent)
198 : QObject (parent)
199 , d (new StyleLspPrivate())
200{
201 auto &ctx = dpfInstance.serviceContext();
202 auto windowService = ctx.service<WindowService>(WindowService::name());
203 if (!windowService)
204 return;
205 d->edit = parent;
206
207 setIndicStyle();
208 setMargin();
209
210 QObject::connect(d->edit, &ScintillaEditExtern::charAdded, this, &StyleLsp::SciCharAdded);
211 QObject::connect(d->edit, &ScintillaEditExtern::linesAdded, this, &StyleLsp::sciLinesAdded);
212 QObject::connect(d->edit, &ScintillaEditExtern::textInserted, this, &StyleLsp::sciTextInsertedTotal);
213 QObject::connect(d->edit, &ScintillaEditExtern::textDeleted, this, &StyleLsp::sciTextDeletedTotal);
214 QObject::connect(d->edit, &ScintillaEditExtern::hovered, this, &StyleLsp::sciHovered);
215 QObject::connect(d->edit, &ScintillaEditExtern::hoverCleaned, this, &StyleLsp::sciHoverCleaned);
216 QObject::connect(d->edit, &ScintillaEditExtern::definitionHover, this, &StyleLsp::sciDefinitionHover);
217 QObject::connect(d->edit, &ScintillaEditExtern::definitionHoverCleaned, this, &StyleLsp::sciDefinitionHoverCleaned);
218 QObject::connect(d->edit, &ScintillaEditExtern::indicClicked, this, &StyleLsp::sciIndicClicked);
219 QObject::connect(d->edit, &ScintillaEditExtern::indicReleased, this, &StyleLsp::sciIndicReleased);
220 QObject::connect(d->edit, &ScintillaEditExtern::selectionMenu, this, &StyleLsp::sciSelectionMenu);
221 QObject::connect(d->edit, &ScintillaEditExtern::replaceed, this, &StyleLsp::sciReplaced);
222 QObject::connect(d->edit, &ScintillaEditExtern::fileClosed, this, &StyleLsp::sciClosed);
223 QObject::connect(&d->renamePopup, &RenamePopup::editingFinished, this, &StyleLsp::renameRequest, Qt::UniqueConnection);
224 QObject::connect(CodeLens::instance(), &CodeLens::doubleClicked,
225 this, [=](const QString &filePath, const lsp::Range &range){
226 newlsp::Range newRange;
227 newRange.start.line = range.start.line;
228 newRange.start.character = range.start.character;
229 newRange.end.line = range.end.line;
230 newRange.end.character = range.end.character;
231 EditorCallProxy::instance()->toJumpFileLineWithKey(d->edit->projectKey(), filePath, editLineNumber(range.start.line));
232 });
233
234 if (!d->rangeFormattingAction) {
235 d->rangeFormattingAction = new QAction(tr("Range Formatting"));
236 ActionManager::getInstance()->registerAction(d->rangeFormattingAction, "Tool.Range.Formatting",
237 tr("Range Formatting"), QKeySequence(Qt::Modifier::CTRL | Qt::Key::Key_I),
238 ":/codeeditor/images/formatting.png");
239 AbstractAction *actionImpl = new AbstractAction(d->rangeFormattingAction);
240 windowService->addAction(MWM_TOOLS, actionImpl);
241 }
242}
243
244TextEdit *StyleLsp::edit()
245{
246 return d->edit;
247}
248
249StyleLsp::~StyleLsp()
250{
251
252}
253
254void StyleLsp::initLspConnection()
255{
256 if (!d->edit) {
257 return;
258 }
259
260 QObject::connect(d->getClient(), QOverload<const lsp::References&>::of(&newlsp::Client::requestResult),
261 CodeLens::instance(), &CodeLens::displayReference, Qt::UniqueConnection);
262
263 //bind signals to file diagnostics
264 QObject::connect(d->getClient(), QOverload<const newlsp::PublishDiagnosticsParams &>::of(&newlsp::Client::publishDiagnostics),
265 this, [=](const newlsp::PublishDiagnosticsParams &data) {this->setDiagnostics(data);});
266
267 QObject::connect(d->getClient(), QOverload<const QList<lsp::Data>&>::of(&newlsp::Client::requestResult),
268 this, &StyleLsp::setTokenFull);
269
270 QObject::connect(d->getClient(), QOverload<const newlsp::Hover&>::of(&newlsp::Client::hoverRes),
271 this, &StyleLsp::setHover);
272
273 QObject::connect(d->getClient(), QOverload<const lsp::CompletionProvider&>::of(&newlsp::Client::requestResult),
274 this, [=](const lsp::CompletionProvider& provider){
275 d->completionCache.provider = provider;
276 });
277
278 QObject::connect(d->getClient(), &newlsp::Client::rangeFormattingRes,
279 this, &StyleLsp::rangeFormattingReplace);
280
281 /* to use QOverload cast virtual slot can't working */
282 QObject::connect(d->getClient(), QOverload<const newlsp::Location&>::of(&newlsp::Client::definitionRes),
283 this, QOverload<const newlsp::Location&>::of(&StyleLsp::setDefinition));
284 QObject::connect(d->getClient(), QOverload<const std::vector<newlsp::Location>&>::of(&newlsp::Client::definitionRes),
285 this, QOverload<const std::vector<newlsp::Location> &>::of(&StyleLsp::setDefinition));
286 QObject::connect(d->getClient(), QOverload<const std::vector<newlsp::LocationLink>&>::of(&newlsp::Client::definitionRes),
287 this, QOverload<const std::vector<newlsp::LocationLink>&>::of(&StyleLsp::setDefinition));
288
289 if (d->getClient()) {
290 qApp->metaObject()->invokeMethod(d->getClient(), "openRequest", Qt::QueuedConnection, Q_ARG(const QString &, d->edit->file()));
291 qApp->metaObject()->invokeMethod(d->getClient(), "docSemanticTokensFull", Qt::QueuedConnection, Q_ARG(const QString &, d->edit->file()));
292 }
293}
294
295int StyleLsp::getLspCharacter(sptr_t doc, sptr_t sciPosition)
296{
297 return getLspPosition(doc, sciPosition).character;
298}
299
300void StyleLsp::sciTextInsertedTotal(Scintilla::Position position,
301 Scintilla::Position length, Scintilla::Position linesAdded,
302 const QByteArray &text, Scintilla::Position line)
303{
304 if (!d->edit || !d->getClient())
305 return;
306
307 if (d->textChangedTimer.isActive()) {
308 d->textChangedTimer.stop();
309 }
310
311 // 补全
312 auto wordStartPos = d->edit->wordStartPosition(position - length, true);
313 setCompletion(d->edit->textRange(wordStartPos, position).replace(" ","") + text,
314 position - wordStartPos, d->completionCache.provider);
315
316 if (position == d->textChangedCache.positionCache + d->textChangedCache.lengthCache) {
317 d->textChangedCache.lengthCache += length;
318 d->textChangedCache.textCache += text;
319 } else {
320 d->textChangedCache.positionCache = position;
321 d->textChangedCache.lengthCache = length;
322 d->textChangedCache.textCache = text;
323 d->textChangedCache.state = TextChangeCache::State::Inserted; //设置增量标志
324 }
325
326 d->textChangedTimer.start(500);
327 QObject::connect(&d->textChangedTimer, &QTimer::timeout,
328 this, &StyleLsp::sciTextChangedTotal,
329 Qt::UniqueConnection);
330}
331
332void StyleLsp::sciTextDeletedTotal(Scintilla::Position position,
333 Scintilla::Position length, Scintilla::Position linesAdded,
334 const QByteArray &text, Scintilla::Position line)
335{
336 if (!d->edit || !d->getClient())
337 return;
338
339 if (d->textChangedTimer.isActive()) {
340 d->textChangedTimer.stop();
341 }
342
343 if (d->textChangedCache.lengthCache == 0) { // 性质跳变
344 d->textChangedCache.state = TextChangeCache::State::Deleted; // 设置为减量
345 d->textChangedCache.positionCache = position + length;
346 d->textChangedCache.lengthCache = length;
347 d->textChangedCache.textCache.insert(0, text);
348 }
349
350 // 增量删除
351 if (d->textChangedCache.state == TextChangeCache::State::Inserted) {
352 if (d->textChangedCache.positionCache + d->textChangedCache.lengthCache - length == position && 0 != position) {
353 d->textChangedCache.textCache.remove(d->textChangedCache.textCache.size() - length,
354 d->textChangedCache.textCache.size());
355 d->textChangedCache.lengthCache -= length;
356 d->textChangedCache.state = TextChangeCache::State::Inserted; // 设置增量标志
357 // // 补全
358 // auto wordStartPos = d->edit->wordStartPosition(position, true);
359 // auto wordEndPos = d->edit->wordEndPosition(position, true);
360 // qInfo() << "\npositionCache" << d->textChangedCache.positionCache
361 // << "\ntextCache" << d->textChangedCache.textCache
362 // << "\nlengthCache" << d->textChangedCache.lengthCache
363 // << "\nstate" << d->textChangedCache.state
364 // << "\nwodStartPos" << wordStartPos
365 // << "\nwodEndPos" << wordEndPos
366 // << "textRange" << d->edit->textRange(wordStartPos, wordEndPos);
367 // setCompletion(d->edit->textRange(wordStartPos, wordEndPos),
368 // wordStartPos, d->completionCache.provider);
369 }
370 } else if (d->textChangedCache.state == TextChangeCache::State::Deleted){
371 if (d->textChangedCache.positionCache == position + d->textChangedCache.lengthCache) {
372 d->textChangedCache.lengthCache += length; // 删除文字数量统计
373 d->textChangedCache.textCache.insert(0, text); // 删除文字总览
374 }
375 }
376
377 d->textChangedTimer.start(500);
378 QObject::connect(&d->textChangedTimer, &QTimer::timeout,
379 this, &StyleLsp::sciTextChangedTotal,
380 Qt::UniqueConnection);
381}
382
383void StyleLsp::sciLinesAdded(Scintilla::Position position)
384{
385 Q_UNUSED(position)
386 //notiong to do
387}
388
389void StyleLsp::SciCharAdded(int ch)
390{
391 if (ch == '\n') {
392 auto currLine = d->edit->lineFromPosition(d->edit->currentPos());
393 if (currLine > 0){
394 auto upLineIndentation = d->edit->lineIndentation(currLine - 1);
395 int count = 0;
396 std::string indent;
397 while (count < upLineIndentation) {
398 indent += " ";
399 count ++;
400 }
401 d->edit->addText(indent.size(), indent.c_str());
402 }
403 }
404}
405
406void StyleLsp::sciTextChangedTotal()
407{
408 if (d->textChangedTimer.isActive())
409 d->textChangedTimer.stop();
410
411 if (!d->edit)
412 return;
413
414 d->edit->indicatorClearRange(0, d->edit->length()); // clean all indicator range style
415 cleanDiagnostics();
416
417 if (d->getClient()) {
418 qApp->metaObject()->invokeMethod(d->getClient(), "changeRequest",
419 Q_ARG(const QString &, d->edit->file()),
420 Q_ARG(const QByteArray &, d->edit->textRange(0, d->edit->length())));
421 qApp->metaObject()->invokeMethod(d->getClient(), "completionRequest",
422 Q_ARG(const QString &, d->edit->file()),
423 Q_ARG(const lsp::Position &, getLspPosition(d->edit->docPointer(), d->textChangedCache.positionCache)));
424 qApp->metaObject()->invokeMethod(d->getClient(), "docSemanticTokensFull",
425 Q_ARG(const QString &, d->edit->file()));
426 }
427};
428
429void StyleLsp::sciHovered(Scintilla::Position position)
430{
431 if (!d->edit)
432 return;
433
434 if (d->edit->isLeave())
435 return;
436
437 d->hoverCache.setSciPosition(position);
438
439 if(d->getClient()) {
440 qApp->metaObject()->invokeMethod(d->getClient(), "docHoverRequest",
441 Q_ARG(const QString &, d->edit->file()),
442 Q_ARG(const lsp::Position &, getLspPosition(d->edit->docPointer(), d->hoverCache.getSciPosition())));
443 }
444}
445
446void StyleLsp::sciHoverCleaned(Scintilla::Position position)
447{
448 Q_UNUSED(position);
449 if (!d->edit)
450 return;
451
452 cleanHover();
453 d->hoverCache.clean();
454}
455
456void StyleLsp::sciDefinitionHover(Scintilla::Position position)
457{
458 if (!d->edit || d->edit->isLeave())
459 return;
460
461 // 判断缓存文字范围
462 auto afterTextRange = d->definitionCache.getTextRange();
463 auto currTextRange = SciRangeCache{d->edit->wordStartPosition(position, true), d->edit->wordEndPosition(position, true)};
464 auto isSameTextRange = afterTextRange == currTextRange;
465
466 if (isSameTextRange) { // 相同的关键字不再触发Definition的绘制
467 d->definitionCache.setSciPosition(position); // 更新坐标点
468 return;
469 } else {
470 d->definitionCache.setTextRange(currTextRange);
471 d->definitionCache.setSciPosition(position);
472 d->definitionCache.cleanFromLsp();
473 }
474
475 auto lspPostion = getLspPosition(d->edit->docPointer(), d->definitionCache.getSciPosition());
476 if (d->getClient()){
477 qApp->metaObject()->invokeMethod(d->getClient(), "definitionRequest",
478 Q_ARG(const QString &, d->edit->file()),
479 Q_ARG(const lsp::Position &, lspPostion));
480 }
481}
482
483void StyleLsp::sciDefinitionHoverCleaned(Scintilla::Position position)
484{
485 Q_UNUSED(position);
486 if (!d->edit)
487 return;
488
489 // 判断缓存文字范围
490 auto afterTextRange = d->definitionCache.getTextRange();
491 auto currTextRange = SciRangeCache{d->edit->wordStartPosition(position, true), d->edit->wordEndPosition(position, true)};
492 auto isSameTextRange = afterTextRange == currTextRange;
493 if (!d->definitionCache.isEmpty() && !isSameTextRange) {
494 if (d->edit) {
495 cleanDefinition(d->definitionCache.getSciPosition());
496 }
497 d->definitionCache.clean();
498 }
499}
500
501void StyleLsp::sciIndicClicked(Scintilla::Position position)
502{
503 Q_UNUSED(position);
504 if (!d->edit)
505 return;
506
507 std::bitset<32> flags(d->edit->indicatorAllOnFor(position));
508 if (flags[INDIC_COMPOSITIONTHICK]) {
509 if (d->definitionCache.getLocations().size() > 0) {
510 auto one = d->definitionCache.getLocations().front();
511 EditorCallProxy::instance()->toJumpFileLineWithKey(d->edit->projectKey(),
512 QUrl(QString::fromStdString(one.uri)).toLocalFile(),
513 editLineNumber(one.range.end.line));
514 cleanDefinition(position);
515 } else if (d->definitionCache.getLocationLinks().size() > 0) {
516 auto one = d->definitionCache.getLocationLinks().front();
517 EditorCallProxy::instance()->toJumpFileLineWithKey(d->edit->projectKey(),
518 QUrl(QString::fromStdString(one.targetUri)).toLocalFile(),
519 editLineNumber(one.targetRange.end.line));
520 cleanDefinition(position);
521 } else {
522 auto one = d->definitionCache.getLocation();
523 EditorCallProxy::instance()->toJumpFileLineWithKey(d->edit->projectKey(),
524 QUrl(QString::fromStdString(one.uri)).toLocalFile(),
525 editLineNumber(one.range.end.line));
526 cleanDefinition(position);
527 }
528 }
529}
530
531void StyleLsp::sciIndicReleased(Scintilla::Position position)
532{
533 Q_UNUSED(position);
534}
535
536void StyleLsp::sciSelectionMenu(QContextMenuEvent *event)
537{
538 if (!d->edit)
539 return;
540
541 d->renameCache.setStart(d->edit->selectionStart());
542 d->renameCache.setEnd(d->edit->selectionEnd());
543
544 QPoint showPos = d->edit->mapToGlobal(event->pos());
545 QByteArray sourceText = d->edit->textRange(
546 d->edit->wordStartPosition(d->edit->selectionStart(), true),
547 d->edit->wordEndPosition(d->edit->selectionEnd(), true));
548
549 QMenu contextMenu;
550 QMenu refactor(QMenu::tr("Refactor"));
551
552 QAction *renameAction = refactor.addAction(QAction::tr("Rename"));
553 QObject::connect(renameAction, &QAction::triggered, [&](){
554 d->renamePopup.setOldName(sourceText);
555 d->renamePopup.exec(showPos);
556 });
557 contextMenu.addMenu(&refactor);
558
559 QAction *findUsageAction = contextMenu.addAction(QAction::tr("Find Usages"));
560 QObject::connect(findUsageAction, &QAction::triggered, [&](){
561 if (d->getClient()) {
562 auto lspPos = getLspPosition(d->edit->docPointer(), d->edit->selectionStart());
563 qApp->metaObject()->invokeMethod(d->getClient(), "referencesRequest",
564 Q_ARG(const QString &, d->edit->file()),
565 Q_ARG(const lsp::Position &, lspPos));
566 }
567 });
568
569 contextMenu.addAction(d->rangeFormattingAction);
570 QObject::connect(d->rangeFormattingAction, &QAction::triggered, [&](){
571 if (d->getClient()) {
572 auto selStart = getLspPosition(d->edit->docPointer(), d->edit->selectionStart());
573 auto selEnd = getLspPosition(d->edit->docPointer(), d->edit->selectionEnd());
574 newlsp::Position newSelStart = {selStart.line, selStart.character};
575 newlsp::Position newSelEnd = {selEnd.line, selEnd.character};
576 newlsp::DocumentRangeFormattingParams params;
577 params.textDocument.uri = QUrl::fromLocalFile(d->edit->file())
578 .toString().toStdString();
579 params.range = newlsp::Range{newSelStart, newSelEnd};
580 params.options.tabSize = 4;
581 params.options.insertSpaces = true;
582 d->formattingFile = d->edit->file();
583 qApp->metaObject()->invokeMethod(d->getClient(), "rangeFormatting",
584 Q_ARG(const newlsp::DocumentRangeFormattingParams &, params));
585 }
586 });
587
588 contextMenu.move(showPos);
589 contextMenu.exec();
590}
591
592void StyleLsp::sciReplaced(const QString &file, Scintilla::Position start, Scintilla::Position end, const QString &text)
593{
594 if (!d->edit || file != d->formattingFile)
595 return;
596 Q_UNUSED(text)
597 Q_UNUSED(start);
598 Q_UNUSED(end);
599 if (d->getClient()) {
600 qApp->metaObject()->invokeMethod(d->getClient(), "changeRequest",
601 Q_ARG(const QString &, file),
602 Q_ARG(const QByteArray &, d->edit->textRange(0, d->edit->length())));
603 }
604}
605
606void StyleLsp::sciClosed(const QString &file)
607{
608 if (d->getClient()) {
609 qApp->metaObject()->invokeMethod(d->getClient(), "closeRequest", Q_ARG(const QString &, file));
610 }
611}
612
613void StyleLsp::renameRequest(const QString &newText)
614{
615 auto sciPostion = d->renameCache.getStart().getSciPosition();
616 if (d->edit) {
617 if (d->getClient()) {
618 qApp->metaObject()->invokeMethod(d->getClient(), "renameRequest",
619 Q_ARG(const QString &, d->edit->file()),
620 Q_ARG(const lsp::Position &, getLspPosition(d->edit->docPointer(), sciPostion)),
621 Q_ARG(const QString &, newText));
622 }
623 }
624}
625
626StyleLsp::IndicStyleExt StyleLsp::symbolIndic(lsp::SemanticTokenType::type_value token,
627 QList<lsp::SemanticTokenType::type_index> modifier)
628{
629 Q_UNUSED(d->edit);
630 Q_UNUSED(token);
631 Q_UNUSED(modifier);
632 return {};
633}
634
635lsp::SemanticTokenType::type_value StyleLsp::tokenToDefine(int token)
636{
637 auto client = d->getClient();
638 if (!client)
639 return {};
640 auto initSecTokensProvider = client->initSecTokensProvider();
641 if (0 <= token && token < initSecTokensProvider.legend.tokenTypes.size())
642 return initSecTokensProvider.legend.tokenTypes[token];
643 return {};
644}
645
646void StyleLsp::setIndicStyle()
647{
648 d->edit->indicSetStyle(INDIC_PLAIN, INDIC_PLAIN);
649 d->edit->indicSetStyle(INDIC_SQUIGGLE, INDIC_SQUIGGLE);
650 d->edit->indicSetStyle(INDIC_TT, INDIC_TT);
651 d->edit->indicSetStyle(INDIC_DIAGONAL, INDIC_DIAGONAL);
652 d->edit->indicSetStyle(INDIC_STRIKE, INDIC_STRIKE);
653 d->edit->indicSetStyle(INDIC_HIDDEN, INDIC_HIDDEN);
654 d->edit->indicSetStyle(INDIC_BOX, INDIC_BOX);
655 d->edit->indicSetStyle(INDIC_ROUNDBOX, INDIC_ROUNDBOX);
656 d->edit->indicSetStyle(INDIC_STRAIGHTBOX, INDIC_STRAIGHTBOX);
657 d->edit->indicSetStyle(INDIC_FULLBOX, INDIC_FULLBOX);
658 d->edit->indicSetStyle(INDIC_DASH, INDIC_DASH);
659 d->edit->indicSetStyle(INDIC_DOTS, INDIC_DOTS);
660 d->edit->indicSetStyle(INDIC_SQUIGGLELOW, INDIC_SQUIGGLELOW);
661 d->edit->indicSetStyle(INDIC_DOTBOX, INDIC_DOTBOX);
662 d->edit->indicSetStyle(INDIC_GRADIENT, INDIC_GRADIENT);
663 d->edit->indicSetStyle(INDIC_GRADIENTCENTRE, INDIC_GRADIENTCENTRE);
664 d->edit->indicSetStyle(INDIC_SQUIGGLEPIXMAP, INDIC_SQUIGGLEPIXMAP);
665 d->edit->indicSetStyle(INDIC_COMPOSITIONTHICK, INDIC_COMPOSITIONTHICK);
666 d->edit->indicSetStyle(INDIC_COMPOSITIONTHIN, INDIC_COMPOSITIONTHIN);
667 d->edit->indicSetStyle(INDIC_TEXTFORE, INDIC_TEXTFORE);
668 d->edit->indicSetStyle(INDIC_POINT, INDIC_POINT);
669 d->edit->indicSetStyle(INDIC_POINTCHARACTER, INDIC_POINTCHARACTER);
670}
671
672void StyleLsp::setMargin()
673{
674 d->edit->setMargins(SC_MAX_MARGIN);
675 d->edit->setMarginTypeN(Margin::LspCustom, SC_MARGIN_SYMBOL);
676 d->edit->setMarginWidthN(Margin::LspCustom, 16);
677 d->edit->setMarginMaskN(Margin::LspCustom, 1 << MarkerNumber::Error | 1 << MarkerNumber::ErrorLineBackground
678 | 1 << MarkerNumber::Warning | 1 << MarkerNumber::WarningLineBackground
679 | 1 << MarkerNumber::Information | 1 << MarkerNumber::InformationLineBackground
680 | 1 << MarkerNumber::Hint | 1 << MarkerNumber::HintLineBackground);
681
682 d->edit->markerDefine(MarkerNumber::Error, SC_MARK_CIRCLE);
683 d->edit->markerDefine(MarkerNumber::Warning, SC_MARK_CIRCLE);
684 d->edit->markerDefine(MarkerNumber::Information, SC_MARK_CIRCLE);
685 d->edit->markerDefine(MarkerNumber::Hint, SC_MARK_CIRCLE);
686
687 d->edit->markerDefine(MarkerNumber::ErrorLineBackground, SC_MARK_BACKGROUND);
688 d->edit->markerDefine(MarkerNumber::WarningLineBackground, SC_MARK_BACKGROUND);
689 d->edit->markerDefine(MarkerNumber::InformationLineBackground, SC_MARK_BACKGROUND);
690 d->edit->markerDefine(MarkerNumber::HintLineBackground, SC_MARK_BACKGROUND);
691
692 d->edit->markerSetFore(MarkerNumber::Error, StyleColor::color(StyleColor::Table::get()->Red));
693 d->edit->markerSetBackTranslucent(MarkerNumber::Error, 0);
694 d->edit->markerSetStrokeWidth(MarkerNumber::Error, 300);
695
696 d->edit->markerSetFore(MarkerNumber::Warning, StyleColor::color(StyleColor::Table::get()->Yellow));
697 d->edit->markerSetBackTranslucent(MarkerNumber::Warning, 0);
698 d->edit->markerSetStrokeWidth(MarkerNumber::Warning, 300);
699
700 d->edit->markerSetFore(MarkerNumber::Information, StyleColor::color(StyleColor::Table::get()->Yellow));
701 d->edit->markerSetBackTranslucent(MarkerNumber::Information, 0);
702 d->edit->markerSetStrokeWidth(MarkerNumber::Information, 300);
703
704 d->edit->markerSetFore(MarkerNumber::Hint, StyleColor::color(StyleColor::Table::get()->Yellow));
705 d->edit->markerSetBackTranslucent(MarkerNumber::Hint, 0);
706 d->edit->markerSetStrokeWidth(MarkerNumber::Hint, 300);
707
708 d->edit->markerSetFore(MarkerNumber::ErrorLineBackground, StyleColor::color(StyleColor::Table::get()->Red));
709 d->edit->markerSetBack(MarkerNumber::ErrorLineBackground, StyleColor::color(StyleColor::Table::get()->Red));
710 d->edit->markerSetAlpha(MarkerNumber::ErrorLineBackground, 0x22);
711 d->edit->markerSetFore(MarkerNumber::WarningLineBackground, StyleColor::color(StyleColor::Table::get()->Yellow));
712 d->edit->markerSetBack(MarkerNumber::WarningLineBackground, StyleColor::color(StyleColor::Table::get()->Yellow));
713 d->edit->markerSetAlpha(MarkerNumber::WarningLineBackground, 0x22);
714 d->edit->markerSetFore(MarkerNumber::InformationLineBackground, StyleColor::color(StyleColor::Table::get()->Yellow));
715 d->edit->markerSetBack(MarkerNumber::InformationLineBackground, StyleColor::color(StyleColor::Table::get()->Yellow));
716 d->edit->markerSetAlpha(MarkerNumber::InformationLineBackground, 0x22);
717 d->edit->markerSetFore(MarkerNumber::HintLineBackground, StyleColor::color(StyleColor::Table::get()->Yellow));
718 d->edit->markerSetBack(MarkerNumber::HintLineBackground, StyleColor::color(StyleColor::Table::get()->Yellow));
719 d->edit->markerSetAlpha(MarkerNumber::HintLineBackground, 0x22);
720}
721
722void StyleLsp::setDiagnostics(const newlsp::PublishDiagnosticsParams &data)
723{
724 if (!edit())
725 return;
726
727 if (QUrl(QString::fromStdString(data.uri)).toLocalFile() != edit()->file())
728 return;
729
730 this->cleanDiagnostics();
731 for (auto val : data.diagnostics) {
732 if (newlsp::Enum::DiagnosticSeverity::get()->Error == val.severity.value()) { // error
733 newlsp::Position start{val.range.start.line, val.range.start.character};
734 newlsp::Position end{val.range.end.line, val.range.start.character};
735 Sci_Position startPos = getSciPosition(d->edit->docPointer(), start);
736 Sci_Position endPos = getSciPosition(d->edit->docPointer(), end);
737 d->edit->setIndicatorCurrent(INDIC_SQUIGGLE);
738 d->edit->indicSetFore(INDIC_SQUIGGLE, StyleColor::color(StyleColor::Table::get()->Red));
739 d->edit->indicatorFillRange(startPos, endPos - startPos);
740 std::string message = "Error: " + val.message.value();
741 d->edit->eOLAnnotationSetText(val.range.start.line, message.c_str());
742 d->edit->eOLAnnotationSetStyleOffset(EOLAnnotation::RedTextFore);
743 d->edit->eOLAnnotationSetStyle(val.range.start.line, EOLAnnotation::RedTextFore - d->edit->eOLAnnotationStyleOffset());
744 d->edit->styleSetFore(EOLAnnotation::RedTextFore, StyleColor::color(StyleColor::Table::get()->Red));
745 d->edit->eOLAnnotationSetVisible(EOLANNOTATION_STANDARD);
746 d->edit->markerAdd(val.range.start.line, Error);
747 d->edit->markerAdd(val.range.start.line, ErrorLineBackground);
748 }
749 }
750}
751
752void StyleLsp::cleanDiagnostics()
753{
754 d->edit->eOLAnnotationClearAll();
755 for (int line = 0; line < d->edit->lineCount(); line ++) {
756 d->edit->markerDelete(line, Error);
757 d->edit->markerDelete(line, ErrorLineBackground);
758 d->edit->markerDelete(line, Warning);
759 d->edit->markerDelete(line, WarningLineBackground);
760 d->edit->markerDelete(line, Information);
761 d->edit->markerDelete(line, InformationLineBackground);
762 d->edit->markerDelete(line, Hint);
763 d->edit->markerDelete(line, HintLineBackground);
764 }
765}
766
767void StyleLsp::setTokenFull(const QList<lsp::Data> &tokens)
768{
769 qInfo() << Q_FUNC_INFO << tokens.size();
770 if (!edit())
771 return;
772
773 if (!d->edit->lexer())
774 return;
775
776 int cacheLine = 0;
777 for (auto val : tokens) {
778 cacheLine += val.start.line;
779#ifdef QT_DEBUG
780 qInfo() << "line:" << cacheLine;
781 qInfo() << "charStart:" << val.start.character;
782 qInfo() << "charLength:" << val.length;
783 qInfo() << "tokenType:" << val.tokenType;
784 qInfo() << "tokenModifiers:" << val.tokenModifiers;
785#endif
786 auto sciStartPos = StyleLsp::getSciPosition(d->edit->docPointer(), {cacheLine, val.start.character});
787 auto sciEndPos = d->edit->wordEndPosition(sciStartPos, true);
788 auto wordStartPos = d->edit->wordStartPosition(sciStartPos, true);
789 if (sciStartPos == 0 || sciEndPos == d->edit->length() || wordStartPos != sciStartPos)
790 continue;
791 QString sourceText = d->edit->textRange(sciStartPos, sciEndPos);
792 int wordLength = sciEndPos - sciStartPos;
793#ifdef QT_DEBUG
794 qInfo() << "text:" << sourceText;
795#endif
796 if (!sourceText.isEmpty() && wordLength == val.length) {
797 QString tokenValue = tokenToDefine(val.tokenType);
798#ifdef QT_DEBUG
799 qInfo() << "tokenValue:" << tokenValue;
800#endif
801 auto indics = symbolIndic(tokenValue, val.tokenModifiers);
802 for (int i = 0; i < INDIC_MAX; i++) {
803 if (indics.fore.keys().contains(i)) {
804#ifdef QT_DEBUG
805 qInfo() << "fillRangeColor:" << hex << indics.fore[i];
806#endif
807 d->edit->setIndicatorCurrent(i);
808 d->edit->indicSetFlags(i, SC_INDICFLAG_VALUEFORE);
809 d->edit->setIndicatorValue(indics.fore[i]);
810 d->edit->indicatorFillRange(sciStartPos, wordLength);
811 }
812 }
813
814 QString tokenAnnLine = TextEditKeeper::getTokenTypeAnnLine(tokenValue, sourceText);
815 if (!tokenAnnLine.isEmpty()) {
816 editor.setAnnotation(d->edit->file(), cacheLine,
817 QString(TextEditKeeper::userActionAnalyseTitle()),
818 AnnotationInfo{tokenAnnLine});
819 }
820 }
821 }
822}
823
824void StyleLsp::cleanTokenFull()
825{
826 Q_UNUSED(d->edit);
827}
828
829void StyleLsp::setHover(const newlsp::Hover &hover)
830{
831 if (!edit() || edit()->isLeave())
832 return;
833
834 d->edit->callTipSetBack(STYLE_DEFAULT);
835
836 std::string showText;
837 if (newlsp::any_contrast<std::vector<newlsp::MarkedString>>(hover.contents)) {
838 auto markupStrings = std::any_cast<std::vector<newlsp::MarkedString>>(hover.contents);
839 for (auto one : markupStrings) {
840 if (!showText.empty()) showText += "\n";
841
842 if (!one.value.empty()) // markedString value append
843 showText += one.value;
844 else if (!std::string(one).empty()) // markedString self is String append
845 showText += one;
846 };
847 } else if (newlsp::any_contrast<newlsp::MarkupContent>(hover.contents)){
848 auto markupContent = std::any_cast<newlsp::MarkupContent>(hover.contents);
849 showText = markupContent.value;
850 } else if (newlsp::any_contrast<newlsp::MarkedString>(hover.contents)){
851 auto markedString = std::any_cast<newlsp::MarkedString>(hover.contents);
852 if (!std::string(markedString).empty()) {
853 showText = std::string(markedString);
854 } else {
855 showText = markedString.value;
856 }
857 }
858
859 if (hover.range) {
860 // noting to do
861 }
862
863 if (!showText.empty())
864 d->edit->callTipShow(d->hoverCache.getSciPosition(), showText.c_str());
865
866 d->hoverCache.clean();
867}
868
869void StyleLsp::cleanHover()
870{
871 d->edit->callTipCancel();
872}
873
874void StyleLsp::setDefinition(const newlsp::Location &data)
875{
876 if (!edit() || edit()->isLeave())
877 return;
878
879 d->definitionCache.set(data);
880 auto sciStartPos = d->edit->wordStartPosition(d->definitionCache.getSciPosition(), true);
881 auto sciEndPos = d->edit->wordEndPosition(d->definitionCache.getSciPosition(), true);
882
883 setDefinitionSelectedStyle(sciStartPos, sciEndPos);
884}
885
886void StyleLsp::setDefinition(const std::vector<newlsp::Location> &data)
887{
888 if (!edit() || edit()->isLeave())
889 return;
890
891 d->definitionCache.set(data);
892 auto sciStartPos = d->edit->wordStartPosition(d->definitionCache.getSciPosition(), true);
893 auto sciEndPos = d->edit->wordEndPosition(d->definitionCache.getSciPosition(), true);
894
895 if (data.size() >= 1) {
896 setDefinitionSelectedStyle(sciStartPos, sciEndPos);
897 }
898}
899
900void StyleLsp::setDefinition(const std::vector<newlsp::LocationLink> &data)
901{
902 if (!edit() || edit()->isLeave())
903 return;
904
905 d->definitionCache.set(data);
906 auto sciStartPos = d->edit->wordStartPosition(d->definitionCache.getSciPosition(), true);
907 auto sciEndPos = d->edit->wordEndPosition(d->definitionCache.getSciPosition(), true);
908
909 if (data.size() >= 1) {
910 setDefinitionSelectedStyle(sciStartPos, sciEndPos);
911 }
912}
913
914void StyleLsp::cleanDefinition(const Scintilla::Position &pos)
915{
916 std::bitset<32> flags(d->edit->indicatorAllOnFor(pos));
917 if (flags[INDIC_COMPOSITIONTHICK]) {
918 // auto hotSpotStart = d->edit->indicatorStart(INDIC_COMPOSITIONTHICK, pos);
919 // auto hotSpotEnd = d->edit->indicatorEnd(INDIC_COMPOSITIONTHICK, pos);
920 d->edit->setCursor(d->definitionCache.getCursor());
921 // d->edit->indicatorClearRange(hotSpotStart, hotSpotEnd);
922 d->edit->indicatorClearRange(0, d->edit->length());
923 }
924}
925
926void StyleLsp::rangeFormattingReplace(const std::vector<newlsp::TextEdit> &edits)
927{
928 if (edits.size() <= 0)
929 return;
930
931 for (auto itera = edits.rbegin(); itera != edits.rend(); itera++) {
932 auto sciPosStart = getSciPosition(d->edit->docPointer(), itera->range.start);
933 auto sciPosEnd = getSciPosition(d->edit->docPointer(), itera->range.end);
934 auto newText = QString::fromStdString(itera->newText);
935 QString curFile = d->edit->file();
936 QString curFormatting = d->formattingFile;
937 if (d->edit->file() == d->formattingFile)
938 d->edit->replaceRange(sciPosStart, sciPosEnd, newText);
939 }
940}
941
942void StyleLsp::setDefinitionSelectedStyle(const Scintilla::Position start, const Scintilla::Position end)
943{
944 d->edit->setIndicatorCurrent(INDIC_COMPOSITIONTHICK);
945 d->edit->indicSetFore(INDIC_COMPOSITIONTHICK, d->edit->styleFore(0));
946 d->edit->indicatorFillRange(start, end - start);
947 if (d->edit->cursor() != 8) {
948 d->definitionCache.setCursor(d->edit->cursor());
949 d->edit->setCursor(8); // hand from Scintilla platfrom.h
950 }
951}
952
953void StyleLsp::setCompletion(const QByteArray &text,
954 const Scintilla::Position enterLenght,
955 const lsp::CompletionProvider &provider)
956{
957 if (d->edit->isLeave())
958 return;
959
960 if (provider.items.isEmpty() || d->textChangedCache.textCache.isEmpty())
961 return;
962
963 const unsigned char sep = 0x7C; // "|"
964 d->edit->autoCSetSeparator((sptr_t)sep);
965 QString inserts;
966 for (auto item : provider.items) {
967 if (!item.insertText.startsWith(text))
968 continue;
969 inserts += (item.insertText += sep);
970 }
971 if (inserts.endsWith(sep)){
972 inserts.remove(inserts.count() - 1 , 1);
973 }
974
975 if (inserts.isEmpty())
976 return;
977
978 d->edit->autoCShow(enterLenght, inserts.toUtf8());
979}
980
981newlsp::Client *StyleLspPrivate::getClient() const
982{
983 if (edit) {
984 return LSPClientKeeper::instance()->get(edit->projectKey());
985 }
986 return nullptr;
987}
988