11
11
#include < rapidjson/document.h>
12
12
#include < rapidjson/reader.h>
13
13
14
+ #include < llvm/ADT/STLExtras.h>
15
+
14
16
#include < algorithm>
15
17
#include < stdexcept>
16
18
17
19
using namespace clang ;
18
20
21
+ #if LLVM_VERSION_MAJOR < 15 // llvmorg-15-init-6118-gb39f43775796
22
+ namespace llvm {
23
+ template <typename T, typename E>
24
+ constexpr bool is_contained (std::initializer_list<T> set, const E &e) {
25
+ for (const T &v : set)
26
+ if (v == e)
27
+ return true ;
28
+ return false ;
29
+ }
30
+ }
31
+ #endif
32
+
19
33
MAKE_HASHABLE (ccls::SymbolIdx, t.usr, t.kind);
20
34
21
35
namespace ccls {
@@ -51,23 +65,26 @@ REFLECT_STRUCT(DidChangeWorkspaceFoldersParam, event);
51
65
REFLECT_STRUCT (WorkspaceSymbolParam, query, folders);
52
66
53
67
namespace {
68
+ struct Occur {
69
+ lsRange range;
70
+ Role role;
71
+ };
54
72
struct CclsSemanticHighlightSymbol {
55
73
int id = 0 ;
56
74
SymbolKind parentKind;
57
75
SymbolKind kind;
58
76
uint8_t storage;
59
77
std::vector<std::pair<int , int >> ranges;
60
78
61
- // `lsRanges ` is used to compute `ranges`.
62
- std::vector<lsRange> lsRanges ;
79
+ // `lsOccur ` is used to compute `ranges`.
80
+ std::vector<Occur> lsOccurs ;
63
81
};
64
82
65
83
struct CclsSemanticHighlight {
66
84
DocumentUri uri;
67
85
std::vector<CclsSemanticHighlightSymbol> symbols;
68
86
};
69
- REFLECT_STRUCT (CclsSemanticHighlightSymbol, id, parentKind, kind, storage,
70
- ranges, lsRanges);
87
+ REFLECT_STRUCT (CclsSemanticHighlightSymbol, id, parentKind, kind, storage, ranges);
71
88
REFLECT_STRUCT (CclsSemanticHighlight, uri, symbols);
72
89
73
90
struct CclsSetSkippedRanges {
@@ -76,10 +93,16 @@ struct CclsSetSkippedRanges {
76
93
};
77
94
REFLECT_STRUCT (CclsSetSkippedRanges, uri, skippedRanges);
78
95
96
+ struct SemanticTokensPartialResult {
97
+ std::vector<int > data;
98
+ };
99
+ REFLECT_STRUCT (SemanticTokensPartialResult, data);
100
+
79
101
struct ScanLineEvent {
80
102
Position pos;
81
103
Position end_pos; // Second key when there is a tie for insertion events.
82
104
int id;
105
+ Role role;
83
106
CclsSemanticHighlightSymbol *symbol;
84
107
bool operator <(const ScanLineEvent &o) const {
85
108
// See the comments below when insertion/deletion events are inserted.
@@ -190,6 +213,8 @@ MessageHandler::MessageHandler() {
190
213
bind (" textDocument/rangeFormatting" , &MessageHandler::textDocument_rangeFormatting);
191
214
bind (" textDocument/references" , &MessageHandler::textDocument_references);
192
215
bind (" textDocument/rename" , &MessageHandler::textDocument_rename);
216
+ bind (" textDocument/semanticTokens/full" , &MessageHandler::textDocument_semanticTokensFull);
217
+ bind (" textDocument/semanticTokens/range" , &MessageHandler::textDocument_semanticTokensRange);
193
218
bind (" textDocument/signatureHelp" , &MessageHandler::textDocument_signatureHelp);
194
219
bind (" textDocument/typeDefinition" , &MessageHandler::textDocument_typeDefinition);
195
220
bind (" workspace/didChangeConfiguration" , &MessageHandler::workspace_didChangeConfiguration);
@@ -281,16 +306,16 @@ void emitSkippedRanges(WorkingFile *wfile, QueryFile &file) {
281
306
pipeline::notify (" $ccls/publishSkippedRanges" , params);
282
307
}
283
308
284
- void emitSemanticHighlight (DB *db, WorkingFile *wfile, QueryFile &file) {
309
+ static std::unordered_map<SymbolIdx, CclsSemanticHighlightSymbol> computeSemanticTokens (DB *db, WorkingFile *wfile,
310
+ QueryFile &file) {
285
311
static GroupMatch match (g_config->highlight .whitelist ,
286
312
g_config->highlight .blacklist );
287
313
assert (file.def );
288
- if (wfile->buffer_content .size () > g_config->highlight .largeFileSize ||
289
- !match.matches (file.def ->path ))
290
- return ;
291
-
292
314
// Group symbols together.
293
315
std::unordered_map<SymbolIdx, CclsSemanticHighlightSymbol> grouped_symbols;
316
+ if (!match.matches (file.def ->path ))
317
+ return grouped_symbols;
318
+
294
319
for (auto [sym, refcnt] : file.symbol2refcnt ) {
295
320
if (refcnt <= 0 )
296
321
continue ;
@@ -369,14 +394,14 @@ void emitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
369
394
if (std::optional<lsRange> loc = getLsRange (wfile, sym.range )) {
370
395
auto it = grouped_symbols.find (sym);
371
396
if (it != grouped_symbols.end ()) {
372
- it->second .lsRanges .push_back (*loc);
397
+ it->second .lsOccurs .push_back ({ *loc, sym. role } );
373
398
} else {
374
399
CclsSemanticHighlightSymbol symbol;
375
400
symbol.id = idx;
376
401
symbol.parentKind = parent_kind;
377
402
symbol.kind = kind;
378
403
symbol.storage = storage;
379
- symbol.lsRanges .push_back (*loc);
404
+ symbol.lsOccurs .push_back ({ *loc, sym. role } );
380
405
grouped_symbols[sym] = symbol;
381
406
}
382
407
}
@@ -387,17 +412,17 @@ void emitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
387
412
int id = 0 ;
388
413
for (auto &entry : grouped_symbols) {
389
414
CclsSemanticHighlightSymbol &symbol = entry.second ;
390
- for (auto &loc : symbol.lsRanges ) {
415
+ for (auto &occur : symbol.lsOccurs ) {
391
416
// For ranges sharing the same start point, the one with leftmost end
392
417
// point comes first.
393
- events.push_back ({loc. start , loc. end , id, &symbol});
418
+ events.push_back ({occur. range . start , occur. range . end , id, occur. role , &symbol});
394
419
// For ranges sharing the same end point, their relative order does not
395
- // matter, therefore we arbitrarily assign loc .end to them. We use
420
+ // matter, therefore we arbitrarily assign occur.range .end to them. We use
396
421
// negative id to indicate a deletion event.
397
- events.push_back ({loc. end , loc. end , ~id, &symbol});
422
+ events.push_back ({occur. range . end , occur. range . end , ~id, occur. role , &symbol});
398
423
id++;
399
424
}
400
- symbol.lsRanges .clear ();
425
+ symbol.lsOccurs .clear ();
401
426
}
402
427
std::sort (events.begin (), events.end ());
403
428
@@ -413,26 +438,33 @@ void emitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
413
438
// Attribute range [events[i-1].pos, events[i].pos) to events[top-1].symbol
414
439
// .
415
440
if (top && !(events[i - 1 ].pos == events[i].pos ))
416
- events[top - 1 ].symbol ->lsRanges .push_back (
417
- {events[i - 1 ].pos , events[i].pos });
441
+ events[top - 1 ].symbol ->lsOccurs .push_back ({{events[i - 1 ].pos , events[i].pos }, events[i].role });
418
442
if (events[i].id >= 0 )
419
443
events[top++] = events[i];
420
444
else
421
445
deleted[~events[i].id ] = 1 ;
422
446
}
447
+ return grouped_symbols;
448
+ }
449
+
450
+ void emitSemanticHighlight (DB *db, WorkingFile *wfile, QueryFile &file) {
451
+ // Disable $ccls/publishSemanticHighlight if semantic tokens support is
452
+ // enabled or the file is too large.
453
+ if (g_config->client .semanticTokensRefresh || wfile->buffer_content .size () > g_config->highlight .largeFileSize )
454
+ return ;
455
+ auto grouped_symbols = computeSemanticTokens (db, wfile, file);
423
456
424
457
CclsSemanticHighlight params;
425
458
params.uri = DocumentUri::fromPath (wfile->filename );
426
459
// Transform lsRange into pair<int, int> (offset pairs)
427
- if (!g_config-> highlight . lsRanges ) {
428
- std::vector<std::pair<lsRange , CclsSemanticHighlightSymbol *>> scratch;
460
+ {
461
+ std::vector<std::pair<Occur , CclsSemanticHighlightSymbol *>> scratch;
429
462
for (auto &entry : grouped_symbols) {
430
- for (auto &range : entry.second .lsRanges )
431
- scratch.emplace_back (range , &entry.second );
432
- entry.second .lsRanges .clear ();
463
+ for (auto &occur : entry.second .lsOccurs )
464
+ scratch.push_back ({occur , &entry.second } );
465
+ entry.second .lsOccurs .clear ();
433
466
}
434
- std::sort (scratch.begin (), scratch.end (),
435
- [](auto &l, auto &r) { return l.first .start < r.first .start ; });
467
+ std::sort (scratch.begin (), scratch.end (), [](auto &l, auto &r) { return l.first .range < r.first .range ; });
436
468
const auto &buf = wfile->buffer_content ;
437
469
int l = 0 , c = 0 , i = 0 , p = 0 ;
438
470
auto mov = [&](int line, int col) {
@@ -455,7 +487,7 @@ void emitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
455
487
return c < col;
456
488
};
457
489
for (auto &entry : scratch) {
458
- lsRange &r = entry.first ;
490
+ lsRange &r = entry.first . range ;
459
491
if (mov (r.start .line , r.start .character ))
460
492
continue ;
461
493
int beg = p;
@@ -466,8 +498,84 @@ void emitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
466
498
}
467
499
468
500
for (auto &entry : grouped_symbols)
469
- if (entry.second .ranges .size () || entry.second .lsRanges .size ())
501
+ if (entry.second .ranges .size () || entry.second .lsOccurs .size ())
470
502
params.symbols .push_back (std::move (entry.second ));
471
503
pipeline::notify (" $ccls/publishSemanticHighlight" , params);
472
504
}
505
+
506
+ void MessageHandler::textDocument_semanticTokensFull (TextDocumentParam ¶m, ReplyOnce &reply) {
507
+ SemanticTokensRangeParams parameters{param.textDocument , lsRange{{0 , 0 }, {UINT16_MAX, INT16_MAX}}};
508
+ textDocument_semanticTokensRange (parameters, reply);
509
+ }
510
+
511
+ void MessageHandler::textDocument_semanticTokensRange (SemanticTokensRangeParams ¶m, ReplyOnce &reply) {
512
+ int file_id;
513
+ auto [file, wf] = findOrFail (param.textDocument .uri .getPath (), reply, &file_id);
514
+ if (!wf)
515
+ return ;
516
+
517
+ auto grouped_symbols = computeSemanticTokens (db, wf, *file);
518
+ std::vector<std::pair<Occur, CclsSemanticHighlightSymbol *>> scratch;
519
+ for (auto &entry : grouped_symbols) {
520
+ for (auto &occur : entry.second .lsOccurs )
521
+ scratch.emplace_back (occur, &entry.second );
522
+ entry.second .lsOccurs .clear ();
523
+ }
524
+ std::sort (scratch.begin (), scratch.end (), [](auto &l, auto &r) { return l.first .range < r.first .range ; });
525
+
526
+ SemanticTokensPartialResult result;
527
+ int line = 0 , column = 0 ;
528
+ for (auto &entry : scratch) {
529
+ lsRange &r = entry.first .range ;
530
+ CclsSemanticHighlightSymbol &symbol = *entry.second ;
531
+ if (r.start .line != line)
532
+ column = 0 ;
533
+ result.data .push_back (r.start .line - line);
534
+ line = r.start .line ;
535
+ result.data .push_back (r.start .character - column);
536
+ column = r.start .character ;
537
+ result.data .push_back (r.end .character - r.start .character );
538
+
539
+ int tokenType = (int )symbol.kind , modifier = 0 ;
540
+ if (tokenType == (int )SymbolKind::StaticMethod) {
541
+ tokenType = (int )SymbolKind::Method;
542
+ modifier |= 1 << (int )TokenModifier::Static;
543
+ } else if (tokenType >= (int )SymbolKind::FirstExtension) {
544
+ tokenType += (int )SymbolKind::FirstNonStandard - (int )SymbolKind::FirstExtension;
545
+ }
546
+
547
+ // Set modifiers.
548
+ if (entry.first .role & Role::Declaration)
549
+ modifier |= 1 << (int )TokenModifier::Declaration;
550
+ if (entry.first .role & Role::Definition)
551
+ modifier |= 1 << (int )TokenModifier::Definition;
552
+ if (entry.first .role & Role::Read)
553
+ modifier |= 1 << (int )TokenModifier::Read;
554
+ if (entry.first .role & Role::Write)
555
+ modifier |= 1 << (int )TokenModifier::Write;
556
+ if (symbol.storage == SC_Static)
557
+ modifier |= 1 << (int )TokenModifier::Static;
558
+
559
+ if (llvm::is_contained ({SymbolKind::Constructor, SymbolKind::Field, SymbolKind::Method, SymbolKind::StaticMethod},
560
+ symbol.kind ))
561
+ modifier |= 1 << (int )TokenModifier::ClassScope;
562
+ else if (llvm::is_contained ({SymbolKind::File, SymbolKind::Namespace}, symbol.parentKind ))
563
+ modifier |= 1 << (int )TokenModifier::NamespaceScope;
564
+ else if (llvm::is_contained (
565
+ {SymbolKind::Constructor, SymbolKind::Function, SymbolKind::Method, SymbolKind::StaticMethod},
566
+ symbol.parentKind ))
567
+ modifier |= 1 << (int )TokenModifier::FunctionScope;
568
+
569
+ // Rainbow semantic tokens
570
+ static_assert ((int )TokenModifier::Id0 + 20 < 31 );
571
+ if (int rainbow = g_config->highlight .rainbow )
572
+ modifier |= 1 << ((int )TokenModifier::Id0 + symbol.id % std::min (rainbow, 20 ));
573
+
574
+ result.data .push_back (tokenType);
575
+ result.data .push_back (modifier);
576
+ }
577
+
578
+ reply (result);
579
+ }
580
+
473
581
} // namespace ccls
0 commit comments