5
5
#import < future>
6
6
#import < iostream>
7
7
#import < map>
8
+ #import < set>
8
9
#import < sourcekitd/sourcekitd.h>
9
10
#import < sstream>
10
11
#import < string>
@@ -440,18 +441,6 @@ SwiftCompleter::SwiftCompleter(LogLevel logLevel)
440
441
SwiftCompleter::~SwiftCompleter () {
441
442
}
442
443
443
- // Transform completion flags into diagnostic flags
444
- auto DiagnosticFlagsFromFlags (std::string filename, std::vector<std::string> flags) {
445
- std::vector<std::string> outputFlags;
446
- for (auto &f : flags) {
447
- if (f == filename) {
448
- continue ;
449
- }
450
- outputFlags.push_back (f);
451
- }
452
- return outputFlags;
453
- }
454
-
455
444
const std::string SwiftCompleter::CandidatesForLocationInFile (
456
445
const std::string &filename, int line, int column,
457
446
const std::vector<UnsavedFile> &unsavedFiles,
@@ -461,7 +450,7 @@ const std::string SwiftCompleter::CandidatesForLocationInFile(
461
450
ctx.line = line;
462
451
ctx.column = column;
463
452
ctx.unsavedFiles = unsavedFiles;
464
- ctx.flags = flags;
453
+ ctx.flags = FlagsForCompileCommand ( flags) ;
465
454
466
455
SourceKitService sktService (_logger.level ());
467
456
char *response = NULL ;
@@ -470,14 +459,30 @@ const std::string SwiftCompleter::CandidatesForLocationInFile(
470
459
return response;
471
460
}
472
461
462
+ // Transform completion flags into diagnostic flags
463
+ auto DiagnosticFlagsFromFlags (std::string filename,
464
+ std::vector<std::string> flags) {
465
+ std::vector<std::string> outputFlags;
466
+ // Skip the file - I'm not 100% sure why we need this but it will output
467
+ // weird warnings if not.
468
+ for (auto &f : flags) {
469
+ if (f == filename) {
470
+ continue ;
471
+ }
472
+ outputFlags.push_back (f);
473
+ }
474
+ return outputFlags;
475
+ }
476
+
473
477
const std::string
474
478
SwiftCompleter::DiagnosticsForFile (const std::string &filename,
475
479
const std::vector<UnsavedFile> &unsavedFiles,
476
480
const std::vector<std::string> &flags) {
477
481
CompletionContext ctx;
478
482
ctx.sourceFilename = filename;
479
483
ctx.unsavedFiles = unsavedFiles;
480
- ctx.flags = DiagnosticFlagsFromFlags (filename, flags);
484
+ auto completionFlags = FlagsForCompileCommand (flags);
485
+ ctx.flags = DiagnosticFlagsFromFlags (filename, completionFlags);
481
486
ctx.line = 0 ;
482
487
ctx.column = 0 ;
483
488
@@ -495,3 +500,75 @@ SwiftCompleter::DiagnosticsForFile(const std::string &filename,
495
500
return semaresult;
496
501
}
497
502
} // namespace ssvim
503
+ // namespace ssvim
504
+
505
+ #pragma mark - Command Preparation Logic
506
+
507
+ // Basic flag blacklist is a list of flags that cannot be included in a
508
+ // CompilerInvocation for completion.
509
+ //
510
+ // These flags are a pair in the form
511
+ // __FLAG__ Optional(__VALUE__)
512
+ //
513
+ // For example -c sometimes has a value after it.
514
+ //
515
+ // Only skip __VALUE__ when it doesn't start with -.
516
+
517
+ auto FlagStartToken = ' -' ;
518
+ std::set<std::string> BasicFlagBlacklist{" -c" ,
519
+ " -MP" ,
520
+ " -MD" ,
521
+ " -MMD" ,
522
+ " --fcolor-diagnostics" ,
523
+ " -emit-reference-dependencies-path" ,
524
+ " -emit-dependencies-path" ,
525
+ " -emit-module-path" ,
526
+ " -serialize-diagnostics-path" ,
527
+ " -emit-module-doc-path" ,
528
+ " -frontend" ,
529
+ " -o" };
530
+
531
+ // These flags may specified as pair of the form
532
+ // __FLAG__ __VALUE__
533
+ //
534
+ // Unconditionally exclude flags in this blacklist and the next value
535
+
536
+ std::set<std::string> PairedFlagBlacklist{" -Xcc" };
537
+
538
+ // Take a raw split command and output completion flags
539
+ std::vector<std::string>
540
+ ssvim::FlagsForCompileCommand (std::vector<std::string> flags) {
541
+ if (flags.size () == 0 ) {
542
+ return flags;
543
+ }
544
+
545
+ std::vector<std::string> outFlags;
546
+
547
+ // Skip the first flag if needed
548
+ // Assume that someone will be using a swift compiler with an absolute path
549
+ // and strip it off if so. All compile commands should be formed this way,
550
+ // but some old test code uses it, so leave it for now.
551
+ auto isCompilerBinary = flags.at (0 )[0 ] == ' /' ;
552
+ unsigned long i = isCompilerBinary ? 1 : 0 ;
553
+ auto length = flags.size ();
554
+ while (i < length) {
555
+ auto flag = flags.at (i);
556
+ if (PairedFlagBlacklist.find (flag) != PairedFlagBlacklist.end ()) {
557
+ i = i + 1 ;
558
+ } else if (BasicFlagBlacklist.find (flag) != BasicFlagBlacklist.end ()) {
559
+ auto nextIdx = i + 1 ;
560
+
561
+ // Skip the pair (FLAG, VALUE) when the next value isn't
562
+ // another flag.
563
+ if (nextIdx < length) {
564
+ if (flags[nextIdx][0 ] != FlagStartToken) {
565
+ i = i + 1 ;
566
+ }
567
+ }
568
+ } else {
569
+ outFlags.push_back (flag);
570
+ }
571
+ i = i + 1 ;
572
+ }
573
+ return outFlags;
574
+ }
0 commit comments