Sprint 277: LSP Advanced Orchestration (steps 1913-1917)
- LSPWorkspaceIndex: track open docs, URI→{languageId,content,version}
- LSPDiagnosticsAggregator: merge diagnostics from multiple LSP sources per URI
- LSPCompletionMerger: merge+deduplicate completion items from multiple sources
- LSPOrchestrator: top-level coordinator composing all three + proxy + cross-lang resolver
- Sprint277IntegrationSummary: poly-api (Go+TypeScript) end-to-end integration
26/26 tests passing (5+5+5+5+6). All new headers ≤600 lines.
LoRA recorded: sprint277-2026-03-01 (1 call, 569 tokens).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
106
editor/tests/step1915_test.cpp
Normal file
106
editor/tests/step1915_test.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
// Step 1915: LSPCompletionMerger
|
||||
// Collects CompletionItem lists from multiple sources; deduplicates by label.
|
||||
//
|
||||
// t1: addItems and getItems from one source
|
||||
// t2: two sources with disjoint labels → both present
|
||||
// t3: two sources with overlapping label → deduplicated
|
||||
// t4: clearSource removes one source without affecting others
|
||||
// t5: clearAll empties; uniqueCount and sourceCount
|
||||
|
||||
#include "LSPCompletionMerger.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace ws = whetstone;
|
||||
|
||||
static int p=0,f=0;
|
||||
#define T(n) { std::cout<<" "<<#n<<"... "; }
|
||||
#define P() { std::cout<<"PASS\n"; ++p; }
|
||||
#define F(m) { std::cout<<"FAIL: "<<m<<"\n"; ++f; }
|
||||
#define C(c,m) if(!(c)){F(m);return;}
|
||||
|
||||
static ws::CompletionItem ci(const std::string& label, int kind,
|
||||
const std::string& detail,
|
||||
const std::string& src) {
|
||||
ws::CompletionItem i; i.label=label; i.kind=kind; i.detail=detail; i.source=src; return i;
|
||||
}
|
||||
|
||||
void t1(){
|
||||
T(addItems_and_getItems_one_source);
|
||||
ws::LSPCompletionMerger m;
|
||||
m.addItems("gopls", { ci("fmt.Println", 3, "func", "gopls"),
|
||||
ci("fmt.Printf", 3, "func", "gopls") });
|
||||
auto items = m.getItems();
|
||||
C(items.size() == 2, "two items");
|
||||
// sorted by label: fmt.Printf < fmt.Println
|
||||
C(items[0].label == "fmt.Printf", "first item sorted");
|
||||
C(items[1].label == "fmt.Println", "second item sorted");
|
||||
C(m.sourceCount() == 1, "one source");
|
||||
P();
|
||||
}
|
||||
|
||||
void t2(){
|
||||
T(two_sources_disjoint_labels);
|
||||
ws::LSPCompletionMerger m;
|
||||
m.addItems("gopls", { ci("fmt.Println", 3, "func", "gopls") });
|
||||
m.addItems("tsserver", { ci("console.log", 3, "func", "tsserver") });
|
||||
auto items = m.getItems();
|
||||
C(items.size() == 2, "two items total");
|
||||
C(m.uniqueCount() == 2, "uniqueCount 2");
|
||||
bool hasGo = false, hasTs = false;
|
||||
for (auto& i : items) {
|
||||
if (i.label == "fmt.Println") hasGo = true;
|
||||
if (i.label == "console.log") hasTs = true;
|
||||
}
|
||||
C(hasGo && hasTs, "both items present");
|
||||
P();
|
||||
}
|
||||
|
||||
void t3(){
|
||||
T(overlap_deduplicated_first_source_wins);
|
||||
ws::LSPCompletionMerger m;
|
||||
// Both offer "sort" — first by map key order ("gopls" < "tsserver") wins
|
||||
m.addItems("gopls", { ci("sort", 3, "from-go", "gopls") });
|
||||
m.addItems("tsserver", { ci("sort", 3, "from-ts", "tsserver"),
|
||||
ci("split",3, "from-ts", "tsserver") });
|
||||
auto items = m.getItems();
|
||||
C(items.size() == 2, "two unique items");
|
||||
C(items[0].label == "sort", "sort present");
|
||||
C(items[0].source == "gopls", "gopls won duplicate");
|
||||
C(items[1].label == "split", "split present");
|
||||
P();
|
||||
}
|
||||
|
||||
void t4(){
|
||||
T(clearSource_removes_one_source);
|
||||
ws::LSPCompletionMerger m;
|
||||
m.addItems("gopls", { ci("fmt.Println", 3, "func", "gopls") });
|
||||
m.addItems("tsserver", { ci("console.log", 3, "func", "tsserver") });
|
||||
m.clearSource("gopls");
|
||||
auto items = m.getItems();
|
||||
C(items.size() == 1, "one item after clear");
|
||||
C(items[0].label == "console.log", "ts item remains");
|
||||
C(m.sourceCount() == 1, "one source remains");
|
||||
P();
|
||||
}
|
||||
|
||||
void t5(){
|
||||
T(clearAll_and_counts);
|
||||
ws::LSPCompletionMerger m;
|
||||
m.addItems("a", { ci("alpha",1,"","a"), ci("beta",1,"","a") });
|
||||
m.addItems("b", { ci("gamma",1,"","b") });
|
||||
C(m.uniqueCount() == 3, "unique 3");
|
||||
C(m.sourceCount() == 2, "sources 2");
|
||||
m.clearAll();
|
||||
C(m.uniqueCount() == 0, "unique 0 after clearAll");
|
||||
C(m.sourceCount() == 0, "sources 0 after clearAll");
|
||||
C(m.getItems().empty(), "empty after clearAll");
|
||||
P();
|
||||
}
|
||||
|
||||
int main(){
|
||||
std::cout << "Step 1915: LSPCompletionMerger\n";
|
||||
t1(); t2(); t3(); t4(); t5();
|
||||
std::cout << "\n" << p << "/" << (p+f) << " passed\n";
|
||||
return f > 0 ? 1 : 0;
|
||||
}
|
||||
Reference in New Issue
Block a user