@lewin671/lsp-client 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +64 -0
  3. package/lib/common/Feature.d.ts +192 -0
  4. package/lib/common/Feature.js +159 -0
  5. package/lib/common/Feature.js.map +1 -0
  6. package/lib/common/LanguageClient.d.ts +230 -0
  7. package/lib/common/LanguageClient.js +512 -0
  8. package/lib/common/LanguageClient.js.map +1 -0
  9. package/lib/common/features/CompletionFeature.d.ts +23 -0
  10. package/lib/common/features/CompletionFeature.js +101 -0
  11. package/lib/common/features/CompletionFeature.js.map +1 -0
  12. package/lib/common/features/DefinitionFeature.d.ts +23 -0
  13. package/lib/common/features/DefinitionFeature.js +49 -0
  14. package/lib/common/features/DefinitionFeature.js.map +1 -0
  15. package/lib/common/features/DiagnosticFeature.d.ts +15 -0
  16. package/lib/common/features/DiagnosticFeature.js +39 -0
  17. package/lib/common/features/DiagnosticFeature.js.map +1 -0
  18. package/lib/common/features/DocumentSymbolFeature.d.ts +23 -0
  19. package/lib/common/features/DocumentSymbolFeature.js +87 -0
  20. package/lib/common/features/DocumentSymbolFeature.js.map +1 -0
  21. package/lib/common/features/FormattingFeature.d.ts +32 -0
  22. package/lib/common/features/FormattingFeature.js +72 -0
  23. package/lib/common/features/FormattingFeature.js.map +1 -0
  24. package/lib/common/features/HoverFeature.d.ts +23 -0
  25. package/lib/common/features/HoverFeature.js +49 -0
  26. package/lib/common/features/HoverFeature.js.map +1 -0
  27. package/lib/common/features/ReferencesFeature.d.ts +23 -0
  28. package/lib/common/features/ReferencesFeature.js +48 -0
  29. package/lib/common/features/ReferencesFeature.js.map +1 -0
  30. package/lib/common/features/RenameFeature.d.ts +37 -0
  31. package/lib/common/features/RenameFeature.js +72 -0
  32. package/lib/common/features/RenameFeature.js.map +1 -0
  33. package/lib/common/features/index.d.ts +8 -0
  34. package/lib/common/features/index.js +21 -0
  35. package/lib/common/features/index.js.map +1 -0
  36. package/lib/common/utils/index.d.ts +2 -0
  37. package/lib/common/utils/index.js +19 -0
  38. package/lib/common/utils/index.js.map +1 -0
  39. package/lib/common/utils/is.d.ts +15 -0
  40. package/lib/common/utils/is.js +56 -0
  41. package/lib/common/utils/is.js.map +1 -0
  42. package/lib/common/utils/uuid.d.ts +17 -0
  43. package/lib/common/utils/uuid.js +87 -0
  44. package/lib/common/utils/uuid.js.map +1 -0
  45. package/lib/demo/custom-lsp/client.d.ts +1 -0
  46. package/lib/demo/custom-lsp/client.js +86 -0
  47. package/lib/demo/custom-lsp/client.js.map +1 -0
  48. package/lib/demo/custom-lsp/server.d.ts +1 -0
  49. package/lib/demo/custom-lsp/server.js +64 -0
  50. package/lib/demo/custom-lsp/server.js.map +1 -0
  51. package/lib/demo/go-demo/client.d.ts +14 -0
  52. package/lib/demo/go-demo/client.js +697 -0
  53. package/lib/demo/go-demo/client.js.map +1 -0
  54. package/lib/demo/typescript-demo/client.d.ts +14 -0
  55. package/lib/demo/typescript-demo/client.js +680 -0
  56. package/lib/demo/typescript-demo/client.js.map +1 -0
  57. package/lib/demo/typescript-demo/sample-project/src/index.d.ts +9 -0
  58. package/lib/demo/typescript-demo/sample-project/src/index.js +65 -0
  59. package/lib/demo/typescript-demo/sample-project/src/index.js.map +1 -0
  60. package/lib/demo/typescript-demo/sample-project/src/math.d.ts +38 -0
  61. package/lib/demo/typescript-demo/sample-project/src/math.js +63 -0
  62. package/lib/demo/typescript-demo/sample-project/src/math.js.map +1 -0
  63. package/lib/demo/typescript-demo/sample-project/src/models/person.d.ts +41 -0
  64. package/lib/demo/typescript-demo/sample-project/src/models/person.js +53 -0
  65. package/lib/demo/typescript-demo/sample-project/src/models/person.js.map +1 -0
  66. package/lib/demo/typescript-demo/sample-project/src/services/calculator.d.ts +39 -0
  67. package/lib/demo/typescript-demo/sample-project/src/services/calculator.js +69 -0
  68. package/lib/demo/typescript-demo/sample-project/src/services/calculator.js.map +1 -0
  69. package/lib/demo/typescript-demo/sample-project/src/services/user-service.d.ts +40 -0
  70. package/lib/demo/typescript-demo/sample-project/src/services/user-service.js +88 -0
  71. package/lib/demo/typescript-demo/sample-project/src/services/user-service.js.map +1 -0
  72. package/lib/index.d.ts +8 -0
  73. package/lib/index.js +32 -0
  74. package/lib/index.js.map +1 -0
  75. package/lib/interfaces/IHost.d.ts +19 -0
  76. package/lib/interfaces/IHost.js +3 -0
  77. package/lib/interfaces/IHost.js.map +1 -0
  78. package/lib/transports/ITransport.d.ts +8 -0
  79. package/lib/transports/ITransport.js +3 -0
  80. package/lib/transports/ITransport.js.map +1 -0
  81. package/lib/transports/StdioTransport.d.ts +13 -0
  82. package/lib/transports/StdioTransport.js +27 -0
  83. package/lib/transports/StdioTransport.js.map +1 -0
  84. package/package.json +48 -0
@@ -0,0 +1,697 @@
1
+ "use strict";
2
+ /**
3
+ * Go LSP Demo Client
4
+ *
5
+ * This demo shows how to use the lsp-client library to interact with
6
+ * Go Language Server (gopls) and test various LSP features:
7
+ * - Hover
8
+ * - Completion
9
+ * - Go to Definition
10
+ * - Find References
11
+ * - Document Symbols
12
+ * - Rename
13
+ * - Diagnostics
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ const path = require("path");
17
+ const fs = require("fs");
18
+ const LanguageClient_1 = require("../../common/LanguageClient");
19
+ const CompletionFeature_1 = require("../../common/features/CompletionFeature");
20
+ const HoverFeature_1 = require("../../common/features/HoverFeature");
21
+ const DefinitionFeature_1 = require("../../common/features/DefinitionFeature");
22
+ const ReferencesFeature_1 = require("../../common/features/ReferencesFeature");
23
+ const DocumentSymbolFeature_1 = require("../../common/features/DocumentSymbolFeature");
24
+ const RenameFeature_1 = require("../../common/features/RenameFeature");
25
+ const DiagnosticFeature_1 = require("../../common/features/DiagnosticFeature");
26
+ const StdioTransport_1 = require("../../transports/StdioTransport");
27
+ const vscode_languageserver_protocol_1 = require("vscode-languageserver-protocol");
28
+ // Color utilities for console output
29
+ const colors = {
30
+ reset: '\x1b[0m',
31
+ bright: '\x1b[1m',
32
+ green: '\x1b[32m',
33
+ yellow: '\x1b[33m',
34
+ blue: '\x1b[34m',
35
+ magenta: '\x1b[35m',
36
+ cyan: '\x1b[36m',
37
+ red: '\x1b[31m'
38
+ };
39
+ function log(color, prefix, message) {
40
+ console.log(`${color}[${prefix}]${colors.reset} ${message}`);
41
+ }
42
+ function logSection(title) {
43
+ console.log(`\n${colors.bright}${colors.cyan}${'='.repeat(60)}${colors.reset}`);
44
+ console.log(`${colors.bright}${colors.cyan} ${title}${colors.reset}`);
45
+ console.log(`${colors.bright}${colors.cyan}${'='.repeat(60)}${colors.reset}\n`);
46
+ }
47
+ function logSuccess(message) {
48
+ log(colors.green, '✓', message);
49
+ }
50
+ function logInfo(message) {
51
+ log(colors.blue, 'i', message);
52
+ }
53
+ function logWarning(message) {
54
+ log(colors.yellow, '!', message);
55
+ }
56
+ function logError(message) {
57
+ log(colors.red, '✗', message);
58
+ }
59
+ // Sample project paths - resolve from source directory, not lib
60
+ // When running from lib, we need to point to src/demo/go-demo/sample-project
61
+ const PROJECT_ROOT = path.resolve(__dirname, '..', '..', '..');
62
+ const SAMPLE_PROJECT_DIR = path.join(PROJECT_ROOT, 'src', 'demo', 'go-demo', 'sample-project');
63
+ const SAMPLE_FILES = {
64
+ math: path.join(SAMPLE_PROJECT_DIR, 'src', 'math.go'),
65
+ person: path.join(SAMPLE_PROJECT_DIR, 'src', 'person.go'),
66
+ calculator: path.join(SAMPLE_PROJECT_DIR, 'src', 'calculator.go'),
67
+ userService: path.join(SAMPLE_PROJECT_DIR, 'src', 'user_service.go'),
68
+ main: path.join(SAMPLE_PROJECT_DIR, 'src', 'main.go'),
69
+ testDiagnostics: path.join(SAMPLE_PROJECT_DIR, 'src', 'test_diagnostics.go')
70
+ };
71
+ /**
72
+ * Console-based Window implementation
73
+ */
74
+ class ConsoleWindow {
75
+ constructor() {
76
+ this.diagnosticsMap = new Map();
77
+ }
78
+ showMessage(type, message) {
79
+ const typeStr = type === vscode_languageserver_protocol_1.MessageType.Error ? 'ERROR' :
80
+ type === vscode_languageserver_protocol_1.MessageType.Warning ? 'WARNING' : 'INFO';
81
+ log(colors.magenta, `Window.${typeStr}`, message);
82
+ }
83
+ async showMessageRequest(type, message, actions) {
84
+ log(colors.magenta, 'Window.Request', message);
85
+ if (actions && actions.length > 0) {
86
+ logInfo(`Available actions: ${actions.map(a => a.title).join(', ')}`);
87
+ }
88
+ return undefined;
89
+ }
90
+ logMessage(type, message) {
91
+ log(colors.magenta, 'Window.Log', message);
92
+ }
93
+ publishDiagnostics(uri, diagnostics) {
94
+ this.diagnosticsMap.set(uri, diagnostics);
95
+ if (diagnostics.length > 0) {
96
+ logWarning(`Diagnostics for ${path.basename(uri)}: ${diagnostics.length} issue(s)`);
97
+ diagnostics.forEach(d => {
98
+ const severity = d.severity === 1 ? 'Error' : d.severity === 2 ? 'Warning' : 'Info';
99
+ console.log(` - [${severity}] Line ${d.range.start.line + 1}: ${d.message}`);
100
+ });
101
+ }
102
+ }
103
+ getDiagnostics(uri) {
104
+ return this.diagnosticsMap.get(uri) || [];
105
+ }
106
+ }
107
+ /**
108
+ * Workspace implementation pointing to sample project
109
+ */
110
+ class GoWorkspace {
111
+ constructor() {
112
+ this.rootUri = `file://${SAMPLE_PROJECT_DIR}`;
113
+ }
114
+ }
115
+ /**
116
+ * Simple configuration for Go
117
+ */
118
+ class GoConfiguration {
119
+ get(section) {
120
+ // Go specific settings
121
+ if (section === 'go') {
122
+ return {
123
+ lintTool: 'golangci-lint',
124
+ lintFlags: [],
125
+ formatOnSave: true,
126
+ useLanguageServer: true
127
+ };
128
+ }
129
+ return {};
130
+ }
131
+ }
132
+ /**
133
+ * Host implementation
134
+ */
135
+ class GoHost {
136
+ constructor() {
137
+ this.window = new ConsoleWindow();
138
+ this.workspace = new GoWorkspace();
139
+ this.configuration = new GoConfiguration();
140
+ }
141
+ dispose() { }
142
+ }
143
+ /**
144
+ * Read file content
145
+ */
146
+ function readFileContent(filePath) {
147
+ return fs.readFileSync(filePath, 'utf-8');
148
+ }
149
+ /**
150
+ * Convert file path to URI
151
+ */
152
+ function toUri(filePath) {
153
+ return `file://${filePath}`;
154
+ }
155
+ /**
156
+ * Find line and character for a search string in file content
157
+ */
158
+ function findPosition(content, searchStr) {
159
+ const index = content.indexOf(searchStr);
160
+ if (index === -1)
161
+ return null;
162
+ const lines = content.substring(0, index).split('\n');
163
+ return {
164
+ line: lines.length - 1,
165
+ character: lines[lines.length - 1].length
166
+ };
167
+ }
168
+ /**
169
+ * Main demo function
170
+ */
171
+ async function main() {
172
+ console.log(`${colors.bright}${colors.cyan}`);
173
+ console.log('╔════════════════════════════════════════════════════════════╗');
174
+ console.log('║ Go LSP Client Demo ║');
175
+ console.log('║ Testing LSP features with gopls ║');
176
+ console.log('╚════════════════════════════════════════════════════════════╝');
177
+ console.log(colors.reset);
178
+ // Check if sample project exists
179
+ if (!fs.existsSync(SAMPLE_PROJECT_DIR)) {
180
+ logError(`Sample project not found at: ${SAMPLE_PROJECT_DIR}`);
181
+ logInfo('Please make sure the sample-project directory exists.');
182
+ process.exit(1);
183
+ }
184
+ // Find gopls
185
+ const goplsPath = findGopls();
186
+ if (!goplsPath) {
187
+ logError('Could not find Go Language Server (gopls)');
188
+ logInfo('Please install gopls: go install github.com/golang/tools/gopls@latest');
189
+ logInfo('Or: go get -u github.com/golang/tools/gopls');
190
+ process.exit(1);
191
+ }
192
+ logSuccess(`Found gopls at: ${goplsPath}`);
193
+ // Create transport using gopls
194
+ const transport = new StdioTransport_1.StdioTransport(goplsPath, []);
195
+ const window = new ConsoleWindow();
196
+ const host = new GoHost();
197
+ host.window = window; // Store reference for diagnostics access
198
+ const client = new LanguageClient_1.LanguageClient(host, transport, {
199
+ textDocument: {
200
+ hover: { dynamicRegistration: true, contentFormat: ['markdown', 'plaintext'] },
201
+ completion: {
202
+ dynamicRegistration: true,
203
+ completionItem: {
204
+ snippetSupport: true,
205
+ documentationFormat: ['markdown', 'plaintext']
206
+ }
207
+ },
208
+ definition: { dynamicRegistration: true, linkSupport: true },
209
+ references: { dynamicRegistration: true },
210
+ documentSymbol: {
211
+ dynamicRegistration: true,
212
+ hierarchicalDocumentSymbolSupport: true
213
+ },
214
+ rename: { dynamicRegistration: true, prepareSupport: true },
215
+ publishDiagnostics: { relatedInformation: true }
216
+ },
217
+ workspace: {
218
+ workspaceFolders: true
219
+ }
220
+ });
221
+ // Register all features
222
+ const hoverFeature = new HoverFeature_1.HoverFeature(client);
223
+ const completionFeature = new CompletionFeature_1.CompletionFeature(client);
224
+ const definitionFeature = new DefinitionFeature_1.DefinitionFeature(client);
225
+ const referencesFeature = new ReferencesFeature_1.ReferencesFeature(client);
226
+ const documentSymbolFeature = new DocumentSymbolFeature_1.DocumentSymbolFeature(client);
227
+ const renameFeature = new RenameFeature_1.RenameFeature(client);
228
+ const diagnosticFeature = new DiagnosticFeature_1.DiagnosticFeature(client);
229
+ client.registerFeature(hoverFeature);
230
+ client.registerFeature(completionFeature);
231
+ client.registerFeature(definitionFeature);
232
+ client.registerFeature(referencesFeature);
233
+ client.registerFeature(documentSymbolFeature);
234
+ client.registerFeature(renameFeature);
235
+ client.registerFeature(diagnosticFeature);
236
+ try {
237
+ logSection('Starting Language Client');
238
+ await client.start();
239
+ logSuccess('Language client started successfully!');
240
+ // Give server time to initialize
241
+ await sleep(2000);
242
+ // Open documents
243
+ logSection('Opening Documents');
244
+ await openDocuments(client);
245
+ // Wait for server to process documents
246
+ await sleep(2000);
247
+ // Test each feature
248
+ await testHover(client, hoverFeature);
249
+ await testCompletion(client, completionFeature);
250
+ await testDefinition(client, definitionFeature);
251
+ await testReferences(client, referencesFeature);
252
+ await testDocumentSymbols(client, documentSymbolFeature);
253
+ await testRename(client, renameFeature);
254
+ await testDiagnostics(client, diagnosticFeature, window);
255
+ // Final summary
256
+ logSection('Demo Complete');
257
+ logSuccess('All LSP feature tests completed!');
258
+ // Keep alive briefly then stop
259
+ await sleep(1000);
260
+ logInfo('Stopping language client...');
261
+ await client.stop();
262
+ logSuccess('Client stopped gracefully');
263
+ }
264
+ catch (e) {
265
+ logError(`Error: ${e instanceof Error ? e.message : String(e)}`);
266
+ console.error(e);
267
+ process.exit(1);
268
+ }
269
+ }
270
+ /**
271
+ * Find gopls executable
272
+ */
273
+ function findGopls() {
274
+ const possiblePaths = [
275
+ // Local GOPATH
276
+ path.join(process.env.GOPATH || path.join(process.env.HOME || '', 'go'), 'bin', 'gopls'),
277
+ // Common locations
278
+ '/usr/local/bin/gopls',
279
+ '/usr/bin/gopls',
280
+ path.join(process.env.HOME || '', 'go', 'bin', 'gopls'),
281
+ ];
282
+ // Try using which command
283
+ try {
284
+ const { execSync } = require('child_process');
285
+ const result = execSync('which gopls', { encoding: 'utf-8' }).trim();
286
+ if (result && fs.existsSync(result)) {
287
+ return result;
288
+ }
289
+ }
290
+ catch (e) {
291
+ // Ignore
292
+ }
293
+ for (const p of possiblePaths) {
294
+ if (fs.existsSync(p)) {
295
+ return p;
296
+ }
297
+ }
298
+ return null;
299
+ }
300
+ /**
301
+ * Sleep utility
302
+ */
303
+ function sleep(ms) {
304
+ return new Promise(resolve => setTimeout(resolve, ms));
305
+ }
306
+ /**
307
+ * Open sample documents
308
+ */
309
+ async function openDocuments(client) {
310
+ const files = [
311
+ { name: 'math.go', path: SAMPLE_FILES.math },
312
+ { name: 'person.go', path: SAMPLE_FILES.person },
313
+ { name: 'calculator.go', path: SAMPLE_FILES.calculator },
314
+ { name: 'main.go', path: SAMPLE_FILES.main }
315
+ ];
316
+ for (const file of files) {
317
+ if (fs.existsSync(file.path)) {
318
+ const content = readFileContent(file.path);
319
+ client.didOpen({
320
+ textDocument: {
321
+ uri: toUri(file.path),
322
+ languageId: 'go',
323
+ version: 1,
324
+ text: content
325
+ }
326
+ });
327
+ logSuccess(`Opened: ${file.name}`);
328
+ }
329
+ else {
330
+ logWarning(`File not found: ${file.path}`);
331
+ }
332
+ }
333
+ }
334
+ /**
335
+ * Test Hover feature
336
+ */
337
+ async function testHover(client, feature) {
338
+ logSection('Testing Hover Feature');
339
+ if (!feature.isSupported) {
340
+ logWarning('Hover not supported by server');
341
+ return;
342
+ }
343
+ const filePath = SAMPLE_FILES.math;
344
+ const content = readFileContent(filePath);
345
+ // Test hover on 'Add' function
346
+ const addPos = findPosition(content, 'func Add');
347
+ if (addPos) {
348
+ logInfo(`Testing hover at position: line ${addPos.line + 1}, char ${addPos.character + 9}`);
349
+ const hover = await feature.provideHover({
350
+ textDocument: { uri: toUri(filePath) },
351
+ position: { line: addPos.line, character: addPos.character + 5 } // position on 'Add'
352
+ });
353
+ if (hover) {
354
+ logSuccess('Hover result received:');
355
+ printHoverContent(hover);
356
+ }
357
+ else {
358
+ logWarning('No hover information returned');
359
+ }
360
+ }
361
+ // Test hover on 'int' type
362
+ const intPos = findPosition(content, '(a, b int)');
363
+ if (intPos) {
364
+ logInfo(`Testing hover on type annotation`);
365
+ const hover = await feature.provideHover({
366
+ textDocument: { uri: toUri(filePath) },
367
+ position: { line: intPos.line, character: intPos.character + 8 }
368
+ });
369
+ if (hover) {
370
+ logSuccess('Hover on type:');
371
+ printHoverContent(hover);
372
+ }
373
+ }
374
+ }
375
+ /**
376
+ * Print hover content
377
+ */
378
+ function printHoverContent(hover) {
379
+ if (typeof hover.contents === 'string') {
380
+ console.log(` ${hover.contents}`);
381
+ }
382
+ else if (Array.isArray(hover.contents)) {
383
+ hover.contents.forEach(c => {
384
+ if (typeof c === 'string') {
385
+ console.log(` ${c}`);
386
+ }
387
+ else {
388
+ console.log(` [${c.language}] ${c.value}`);
389
+ }
390
+ });
391
+ }
392
+ else if ('kind' in hover.contents) {
393
+ console.log(` ${hover.contents.value.substring(0, 200)}...`);
394
+ }
395
+ else if ('value' in hover.contents) {
396
+ console.log(` [${hover.contents.language}] ${hover.contents.value}`);
397
+ }
398
+ }
399
+ /**
400
+ * Test Completion feature
401
+ */
402
+ async function testCompletion(client, feature) {
403
+ logSection('Testing Completion Feature');
404
+ if (!feature.isSupported) {
405
+ logWarning('Completion not supported by server');
406
+ return;
407
+ }
408
+ const filePath = SAMPLE_FILES.calculator;
409
+ const content = readFileContent(filePath);
410
+ // Find a good position for completion (after method receiver)
411
+ const funcPos = findPosition(content, 'func (c *Calculator)');
412
+ if (funcPos) {
413
+ logInfo(`Testing completion in method`);
414
+ const items = await feature.provideCompletion({
415
+ textDocument: { uri: toUri(filePath) },
416
+ position: { line: funcPos.line + 5, character: 10 }
417
+ });
418
+ if (items) {
419
+ const completionItems = Array.isArray(items) ? items : items.items;
420
+ logSuccess(`Received ${completionItems.length} completion item(s):`);
421
+ completionItems.slice(0, 10).forEach((item) => {
422
+ console.log(` - ${item.label} (${getCompletionKindName(item.kind || 0)})`);
423
+ });
424
+ if (completionItems.length > 10) {
425
+ console.log(` ... and ${completionItems.length - 10} more`);
426
+ }
427
+ }
428
+ else {
429
+ logWarning('No completion items returned');
430
+ }
431
+ }
432
+ }
433
+ /**
434
+ * Get completion kind name
435
+ */
436
+ function getCompletionKindName(kind) {
437
+ const kinds = {
438
+ 1: 'Text', 2: 'Method', 3: 'Function', 4: 'Constructor',
439
+ 5: 'Field', 6: 'Variable', 7: 'Class', 8: 'Interface',
440
+ 9: 'Module', 10: 'Property', 11: 'Unit', 12: 'Value',
441
+ 13: 'Enum', 14: 'Keyword', 15: 'Snippet', 16: 'Color',
442
+ 17: 'File', 18: 'Reference', 19: 'Folder', 20: 'EnumMember',
443
+ 21: 'Constant', 22: 'Struct', 23: 'Event', 24: 'Operator',
444
+ 25: 'TypeParameter'
445
+ };
446
+ return kinds[kind] || 'Unknown';
447
+ }
448
+ /**
449
+ * Test Definition feature
450
+ */
451
+ async function testDefinition(client, feature) {
452
+ logSection('Testing Go to Definition');
453
+ if (!feature.isSupported) {
454
+ logWarning('Definition not supported by server');
455
+ return;
456
+ }
457
+ const filePath = SAMPLE_FILES.calculator;
458
+ const content = readFileContent(filePath);
459
+ // Find usage of 'Add' function
460
+ const addUsage = findPosition(content, 'Add(a, b');
461
+ if (addUsage) {
462
+ logInfo(`Testing go to definition on 'Add' function call`);
463
+ const definition = await feature.provideDefinition({
464
+ textDocument: { uri: toUri(filePath) },
465
+ position: { line: addUsage.line, character: addUsage.character + 1 }
466
+ });
467
+ if (definition) {
468
+ if (Array.isArray(definition)) {
469
+ logSuccess(`Found ${definition.length} definition(s):`);
470
+ definition.forEach((loc) => {
471
+ const uri = 'targetUri' in loc ? loc.targetUri : loc.uri;
472
+ const range = 'targetRange' in loc ? loc.targetRange : loc.range;
473
+ console.log(` - ${path.basename(uri)} at line ${range.start.line + 1}`);
474
+ });
475
+ }
476
+ else {
477
+ const loc = definition;
478
+ logSuccess(`Definition found:`);
479
+ console.log(` - ${path.basename(loc.uri)} at line ${loc.range.start.line + 1}`);
480
+ }
481
+ }
482
+ else {
483
+ logWarning('No definition found');
484
+ }
485
+ }
486
+ // Test definition on Person type
487
+ const filePath2 = SAMPLE_FILES.userService;
488
+ const content2 = readFileContent(filePath2);
489
+ const personUsage = findPosition(content2, '*Person');
490
+ if (personUsage) {
491
+ logInfo(`Testing go to definition on 'Person' type`);
492
+ const definition = await feature.provideDefinition({
493
+ textDocument: { uri: toUri(filePath2) },
494
+ position: { line: personUsage.line, character: personUsage.character + 1 }
495
+ });
496
+ if (definition) {
497
+ logSuccess('Person type definition found');
498
+ }
499
+ }
500
+ }
501
+ /**
502
+ * Test References feature
503
+ */
504
+ async function testReferences(client, feature) {
505
+ logSection('Testing Find References');
506
+ if (!feature.isSupported) {
507
+ logWarning('References not supported by server');
508
+ return;
509
+ }
510
+ const filePath = SAMPLE_FILES.math;
511
+ const content = readFileContent(filePath);
512
+ // Find references to 'Add' function
513
+ const addDef = findPosition(content, 'func Add');
514
+ if (addDef) {
515
+ logInfo(`Finding all references to 'Add' function`);
516
+ const references = await feature.findReferences({
517
+ textDocument: { uri: toUri(filePath) },
518
+ position: { line: addDef.line, character: addDef.character + 5 },
519
+ context: { includeDeclaration: true }
520
+ });
521
+ if (references && references.length > 0) {
522
+ logSuccess(`Found ${references.length} reference(s):`);
523
+ references.forEach((ref) => {
524
+ console.log(` - ${path.basename(ref.uri)} at line ${ref.range.start.line + 1}`);
525
+ });
526
+ }
527
+ else {
528
+ logWarning('No references found');
529
+ }
530
+ }
531
+ }
532
+ /**
533
+ * Test Document Symbols feature
534
+ */
535
+ async function testDocumentSymbols(client, feature) {
536
+ logSection('Testing Document Symbols');
537
+ if (!feature.isSupported) {
538
+ logWarning('Document symbols not supported by server');
539
+ return;
540
+ }
541
+ const filePath = SAMPLE_FILES.person;
542
+ logInfo(`Getting symbols for person.go`);
543
+ const symbols = await feature.getDocumentSymbols({
544
+ textDocument: { uri: toUri(filePath) }
545
+ });
546
+ if (symbols && symbols.length > 0) {
547
+ logSuccess(`Found ${symbols.length} symbol(s):`);
548
+ printSymbols(symbols, 0);
549
+ }
550
+ else {
551
+ logWarning('No symbols found');
552
+ }
553
+ }
554
+ /**
555
+ * Print symbols recursively
556
+ */
557
+ function printSymbols(symbols, indent) {
558
+ const prefix = ' '.repeat(indent);
559
+ symbols.forEach(symbol => {
560
+ if ('children' in symbol) {
561
+ // DocumentSymbol with children
562
+ console.log(`${prefix}- ${symbol.name} (${getSymbolKindName(symbol.kind)})`);
563
+ if (symbol.children) {
564
+ printSymbols(symbol.children, indent + 1);
565
+ }
566
+ }
567
+ else {
568
+ // SymbolInformation
569
+ console.log(`${prefix}- ${symbol.name} (${getSymbolKindName(symbol.kind)})`);
570
+ }
571
+ });
572
+ }
573
+ /**
574
+ * Get symbol kind name
575
+ */
576
+ function getSymbolKindName(kind) {
577
+ const kinds = {
578
+ 1: 'File', 2: 'Module', 3: 'Namespace', 4: 'Package',
579
+ 5: 'Class', 6: 'Method', 7: 'Property', 8: 'Field',
580
+ 9: 'Constructor', 10: 'Enum', 11: 'Interface', 12: 'Function',
581
+ 13: 'Variable', 14: 'Constant', 15: 'String', 16: 'Number',
582
+ 17: 'Boolean', 18: 'Array', 19: 'Object', 20: 'Key',
583
+ 21: 'Null', 22: 'EnumMember', 23: 'Struct', 24: 'Event',
584
+ 25: 'Operator', 26: 'TypeParameter'
585
+ };
586
+ return kinds[kind] || 'Unknown';
587
+ }
588
+ /**
589
+ * Test Rename feature
590
+ */
591
+ async function testRename(client, feature) {
592
+ logSection('Testing Rename');
593
+ if (!feature.isSupported) {
594
+ logWarning('Rename not supported by server');
595
+ return;
596
+ }
597
+ const filePath = SAMPLE_FILES.math;
598
+ const content = readFileContent(filePath);
599
+ // Test prepare rename on 'Add' function
600
+ const addDef = findPosition(content, 'func Add');
601
+ if (addDef && feature.isPrepareSupported) {
602
+ logInfo(`Testing prepare rename on 'Add' function`);
603
+ const prepareResult = await feature.prepareRename({
604
+ textDocument: { uri: toUri(filePath) },
605
+ position: { line: addDef.line, character: addDef.character + 5 }
606
+ });
607
+ if (prepareResult) {
608
+ logSuccess('Prepare rename result:');
609
+ if ('placeholder' in prepareResult) {
610
+ console.log(` Placeholder: ${prepareResult.placeholder}`);
611
+ }
612
+ else if ('defaultBehavior' in prepareResult) {
613
+ console.log(` Default behavior: ${prepareResult.defaultBehavior}`);
614
+ }
615
+ else {
616
+ console.log(` Range: line ${prepareResult.start.line + 1}, char ${prepareResult.start.character}`);
617
+ }
618
+ }
619
+ }
620
+ // Test actual rename (dry run - we won't apply the changes)
621
+ if (addDef) {
622
+ logInfo(`Testing rename 'Add' -> 'AddNumbers' (dry run)`);
623
+ const workspaceEdit = await feature.rename({
624
+ textDocument: { uri: toUri(filePath) },
625
+ position: { line: addDef.line, character: addDef.character + 5 },
626
+ newName: 'AddNumbers'
627
+ });
628
+ if (workspaceEdit) {
629
+ logSuccess('Rename would affect:');
630
+ printWorkspaceEdit(workspaceEdit);
631
+ }
632
+ else {
633
+ logWarning('No rename edits returned');
634
+ }
635
+ }
636
+ }
637
+ /**
638
+ * Test Diagnostics feature
639
+ */
640
+ async function testDiagnostics(client, feature, window) {
641
+ logSection('Testing Diagnostics');
642
+ const filePath = SAMPLE_FILES.testDiagnostics;
643
+ logInfo(`Opening file with errors: test_diagnostics.go`);
644
+ const content = readFileContent(filePath);
645
+ client.didOpen({
646
+ textDocument: {
647
+ uri: toUri(filePath),
648
+ languageId: 'go',
649
+ version: 1,
650
+ text: content
651
+ }
652
+ });
653
+ // Wait for diagnostics to be published
654
+ await sleep(2000);
655
+ // Get diagnostics from the window
656
+ const diagnostics = window.getDiagnostics(toUri(filePath));
657
+ if (diagnostics && diagnostics.length > 0) {
658
+ logSuccess(`Found ${diagnostics.length} diagnostic(s):`);
659
+ diagnostics.forEach((diag, index) => {
660
+ const severity = diag.severity === 1 ? 'Error' :
661
+ diag.severity === 2 ? 'Warning' : 'Info';
662
+ console.log(` ${index + 1}. [${severity}] Line ${diag.range.start.line + 1}, Col ${diag.range.start.character + 1}`);
663
+ console.log(` ${diag.message}`);
664
+ if (diag.code) {
665
+ console.log(` Code: ${diag.code}`);
666
+ }
667
+ });
668
+ }
669
+ else {
670
+ logWarning('No diagnostics returned. This may indicate:');
671
+ logWarning(' - gopls is still processing the file');
672
+ logWarning(' - The errors are not severe enough for diagnostics');
673
+ logWarning(' - Diagnostics feature is not fully supported');
674
+ }
675
+ }
676
+ /**
677
+ * Print workspace edit
678
+ */
679
+ function printWorkspaceEdit(edit) {
680
+ if (edit.changes) {
681
+ for (const [uri, edits] of Object.entries(edit.changes)) {
682
+ console.log(` ${path.basename(uri)}: ${edits.length} edit(s)`);
683
+ edits.slice(0, 3).forEach(e => {
684
+ console.log(` - Line ${e.range.start.line + 1}: "${e.newText}"`);
685
+ });
686
+ if (edits.length > 3) {
687
+ console.log(` ... and ${edits.length - 3} more`);
688
+ }
689
+ }
690
+ }
691
+ if (edit.documentChanges) {
692
+ console.log(` Document changes: ${edit.documentChanges.length} file(s)`);
693
+ }
694
+ }
695
+ // Run the demo
696
+ main().catch(console.error);
697
+ //# sourceMappingURL=client.js.map