Skip to content

Commit 29c83f8

Browse files
committed
Generates telemetry docs
- Adds a new script to generate telemetry docs - Avoids imports (other than constants) in constant files (mostly)
1 parent 76935bd commit 29c83f8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+1332
-259
lines changed

docs/telemetry-events.md

Lines changed: 906 additions & 0 deletions
Large diffs are not rendered by default.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17866,6 +17866,7 @@
1786617866
"build:webviews": "webpack --mode development --config-name webviews",
1786717867
"build:icons": "pnpm run icons:svgo && pnpm run fantasticon && pnpm run icons:apply",
1786817868
"build:tests": "node ./scripts/esbuild.tests.mjs --mode development",
17869+
"generate:docs:telemetry": "node ./scripts/generate-telemetry-docs.mjs",
1786917870
"bundle": "webpack --mode production",
1787017871
"bundle:extension": "webpack --mode production --config-name extension:node",
1787117872
"clean": "pnpx rimraf --glob dist out .vscode-test .vscode-test-web .eslintcache* tsconfig*.tsbuildinfo",

scripts/generate-telemetry-docs.mjs

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// @ts-check
2+
import * as ts from 'typescript';
3+
import * as fs from 'fs';
4+
import * as path from 'path';
5+
import { fileURLToPath } from 'url';
6+
7+
const __filename = fileURLToPath(import.meta.url);
8+
const __dirname = path.join(path.dirname(__filename), '..');
9+
10+
const filePath = path.join(__dirname, 'src/constants.telemetry.ts');
11+
12+
const program = ts.createProgram([filePath], {});
13+
const sourceFile = program.getSourceFile(filePath);
14+
const typeChecker = program.getTypeChecker();
15+
16+
if (!sourceFile) {
17+
throw new Error(`Could not find source file: ${filePath}`);
18+
}
19+
20+
let telemetryEventsType;
21+
let telemetryGlobalContext;
22+
23+
// Find the types
24+
ts.forEachChild(sourceFile, node => {
25+
if (ts.isTypeAliasDeclaration(node)) {
26+
switch (node.name.text) {
27+
case 'TelemetryEvents':
28+
telemetryEventsType = typeChecker.getTypeAtLocation(node);
29+
break;
30+
case 'TelemetryGlobalContext':
31+
telemetryGlobalContext = typeChecker.getTypeAtLocation(node);
32+
break;
33+
}
34+
}
35+
});
36+
37+
if (!telemetryEventsType || !telemetryGlobalContext) {
38+
throw new Error('Could not find the telemetry types');
39+
}
40+
41+
// Generate markdown
42+
let markdown = '# GitLens Telemetry\n\n';
43+
markdown += '> This is a generated file. Do not edit.\n\n';
44+
45+
markdown += '## Global Attributes\n\n';
46+
markdown += '> Global attributes are sent (if available) with every telemetry event\n\n';
47+
48+
markdown += `${expandType(telemetryGlobalContext, '', true, 'global.')}\n\n`;
49+
50+
markdown += '## Events\n\n';
51+
52+
const properties = typeChecker.getPropertiesOfType(telemetryEventsType);
53+
for (const prop of properties) {
54+
const propType = typeChecker.getTypeOfSymbolAtLocation(prop, sourceFile);
55+
56+
markdown += `### ${prop.name}\n\n`;
57+
58+
// Add property documentation if available
59+
const propDocs = prop.getDocumentationComment(typeChecker);
60+
if (propDocs.length > 0) {
61+
markdown += `> ${propDocs.map(doc => doc.text).join('\n> ')}\n\n`;
62+
}
63+
64+
// Check for deprecated tag
65+
const jsDocTags = getJSDocTags(prop);
66+
if (jsDocTags.deprecated) {
67+
markdown += `> **Deprecated:** ${
68+
jsDocTags.deprecated === true ? 'This property is deprecated.' : jsDocTags.deprecated
69+
}\n\n`;
70+
}
71+
72+
markdown += `${expandType(propType, '')}\n\n`;
73+
}
74+
75+
const outputPath = path.join(__dirname, 'docs/telemetry-events.md');
76+
fs.writeFileSync(outputPath, markdown);
77+
78+
function expandType(type, indent = '', isRoot = true, prefix = '') {
79+
let result = '';
80+
81+
if (type.isUnion()) {
82+
if (isRoot) {
83+
return type.types
84+
.map(t => `\`\`\`typescript\n${expandType(t, '', false, prefix)}\n\`\`\``)
85+
.join('\n\nor\n\n');
86+
} else {
87+
const types = type.types.map(t => expandType(t, indent, false, prefix)).join(' | ');
88+
result = types.includes('\n') ? `(${types})` : types;
89+
}
90+
} else if (type.isIntersection()) {
91+
const combinedProperties = new Map();
92+
type.types.forEach(t => {
93+
if (t.symbol && t.symbol.flags & ts.SymbolFlags.TypeLiteral) {
94+
typeChecker.getPropertiesOfType(t).forEach(prop => {
95+
combinedProperties.set(prop.name, prop);
96+
});
97+
}
98+
});
99+
100+
if (combinedProperties.size > 0) {
101+
const expandedProps = Array.from(combinedProperties).map(([name, prop]) => {
102+
const propType = typeChecker.getTypeOfSymbolAtLocation(prop, sourceFile);
103+
const jsDocTags = getJSDocTags(prop);
104+
let propString = '';
105+
if (jsDocTags.deprecated) {
106+
propString += `${indent} // @deprecated: ${
107+
jsDocTags.deprecated === true ? '' : jsDocTags.deprecated
108+
}\n`;
109+
}
110+
propString += `${indent} '${prefix}${name}': ${expandType(propType, indent + ' ', false, prefix)}`;
111+
return propString;
112+
});
113+
result = `{\n${expandedProps.join(',\n')}\n${indent}}`;
114+
} else {
115+
const types = type.types.map(t => expandType(t, indent, false, prefix)).join(' & ');
116+
result = types.includes('\n') ? `(${types})` : types;
117+
}
118+
} else if (type.isStringLiteral()) {
119+
result = `'${type.value}'`;
120+
} else if (type.isNumberLiteral()) {
121+
result = type.value.toString();
122+
} else if (type.symbol && type.symbol.flags & ts.SymbolFlags.Method) {
123+
const signatures = type.getCallSignatures();
124+
if (signatures.length) {
125+
const params = signatures[0]
126+
.getParameters()
127+
.map(
128+
p =>
129+
`'${prefix}${p.name}': ${expandType(
130+
typeChecker.getTypeOfSymbolAtLocation(p, sourceFile),
131+
indent,
132+
false,
133+
prefix,
134+
)}`,
135+
)
136+
.join(', ');
137+
const returnType = expandType(signatures[0].getReturnType(), indent, false, prefix);
138+
result = `(${params}) => ${returnType}`;
139+
}
140+
} else if (type.symbol && type.symbol.flags & ts.SymbolFlags.TypeLiteral) {
141+
const properties = typeChecker.getPropertiesOfType(type);
142+
if (properties.length === 0) {
143+
result = '{}';
144+
} else {
145+
const expandedProps = properties.map(prop => {
146+
const propType = typeChecker.getTypeOfSymbolAtLocation(prop, sourceFile);
147+
const jsDocTags = getJSDocTags(prop);
148+
let propString = '';
149+
if (jsDocTags.deprecated) {
150+
propString += `${indent} // @deprecated: ${
151+
jsDocTags.deprecated === true ? '' : jsDocTags.deprecated
152+
}\n`;
153+
}
154+
propString += `${indent} '${prefix}${prop.name}': ${expandType(
155+
propType,
156+
indent + ' ',
157+
false,
158+
prefix,
159+
)}`;
160+
return propString;
161+
});
162+
result = `{\n${expandedProps.join(',\n')}\n${indent}}`;
163+
}
164+
} else {
165+
result = typeChecker.typeToString(type);
166+
}
167+
168+
if (isRoot && !type.isUnion()) {
169+
return `\`\`\`typescript\n${result}\n\`\`\``;
170+
}
171+
return result;
172+
}
173+
174+
function getJSDocTags(symbol) {
175+
const tags = {};
176+
const jsDocTags = symbol.getJsDocTags();
177+
for (const tag of jsDocTags) {
178+
tags[tag.name] = tag.text ? tag.text.map(t => t.text).join(' ') : true;
179+
}
180+
return tags;
181+
}

src/ai/aiProviderService.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { CancellationToken, Disposable, MessageItem, ProgressOptions, QuickInputButton } from 'vscode';
22
import { env, ThemeIcon, Uri, window } from 'vscode';
3-
import type { AIModels, AIProviders, SupportedAIModels } from '../constants.ai';
3+
import type { AIModels, AIProviders, SupportedAIModels, VSCodeAIModels } from '../constants.ai';
44
import type { AIGenerateDraftEventData, Sources, TelemetryEvents } from '../constants.telemetry';
55
import type { Container } from '../container';
66
import { CancellationError } from '../errors';
@@ -19,7 +19,6 @@ import type { TelemetryService } from '../telemetry/telemetry';
1919
import { AnthropicProvider } from './anthropicProvider';
2020
import { GeminiProvider } from './geminiProvider';
2121
import { OpenAIProvider } from './openaiProvider';
22-
import type { VSCodeAIModels } from './vscodeProvider';
2322
import { isVSCodeAIModel, VSCodeAIProvider } from './vscodeProvider';
2423

2524
export interface AIModel<

src/ai/anthropicProvider.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { fetch } from '@env/fetch';
22
import type { CancellationToken } from 'vscode';
33
import { window } from 'vscode';
4+
import type { AnthropicModels } from '../constants.ai';
45
import type { TelemetryEvents } from '../constants.telemetry';
56
import type { Container } from '../container';
67
import { CancellationError } from '../errors';
@@ -25,15 +26,6 @@ function isSupportedModel(model: AnthropicModel): model is SupportedModel {
2526
return !isLegacyModel(model);
2627
}
2728

28-
export type AnthropicModels =
29-
| 'claude-instant-1'
30-
| 'claude-2'
31-
| 'claude-2.1'
32-
| 'claude-3-opus-20240229'
33-
| 'claude-3-sonnet-20240229'
34-
| 'claude-3-5-sonnet-20240620'
35-
| 'claude-3-haiku-20240307';
36-
3729
type AnthropicModel = AIModel<typeof provider.id>;
3830

3931
const models: AnthropicModel[] = [

src/ai/geminiProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { fetch } from '@env/fetch';
22
import type { CancellationToken } from 'vscode';
33
import { window } from 'vscode';
4+
import type { GeminiModels } from '../constants.ai';
45
import type { TelemetryEvents } from '../constants.telemetry';
56
import type { Container } from '../container';
67
import { CancellationError } from '../errors';
@@ -13,7 +14,6 @@ import { cloudPatchMessageSystemPrompt, codeSuggestMessageSystemPrompt, commitMe
1314

1415
const provider = { id: 'gemini', name: 'Google' } as const;
1516

16-
export type GeminiModels = 'gemini-1.0-pro' | 'gemini-1.5-pro-latest' | 'gemini-1.5-flash-latest';
1717
type GeminiModel = AIModel<typeof provider.id>;
1818
const models: GeminiModel[] = [
1919
{

src/ai/openaiProvider.ts

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { fetch } from '@env/fetch';
22
import type { CancellationToken } from 'vscode';
33
import { window } from 'vscode';
4+
import type { OpenAIModels } from '../constants.ai';
45
import type { TelemetryEvents } from '../constants.telemetry';
56
import type { Container } from '../container';
67
import { CancellationError } from '../errors';
@@ -13,23 +14,6 @@ import { cloudPatchMessageSystemPrompt, codeSuggestMessageSystemPrompt, commitMe
1314

1415
const provider = { id: 'openai', name: 'OpenAI' } as const;
1516

16-
export type OpenAIModels =
17-
| 'gpt-4o'
18-
| 'gpt-4o-mini'
19-
| 'gpt-4-turbo'
20-
| 'gpt-4-turbo-2024-04-09'
21-
| 'gpt-4-turbo-preview'
22-
| 'gpt-4-0125-preview'
23-
| 'gpt-4-1106-preview'
24-
| 'gpt-4'
25-
| 'gpt-4-0613'
26-
| 'gpt-4-32k'
27-
| 'gpt-4-32k-0613'
28-
| 'gpt-3.5-turbo'
29-
| 'gpt-3.5-turbo-0125'
30-
| 'gpt-3.5-turbo-1106'
31-
| 'gpt-3.5-turbo-16k';
32-
3317
type OpenAIModel = AIModel<typeof provider.id>;
3418
const models: OpenAIModel[] = [
3519
{

src/ai/vscodeProvider.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { cloudPatchMessageSystemPrompt, codeSuggestMessageSystemPrompt, commitMe
1111

1212
const provider = { id: 'vscode', name: 'VS Code Provided' } as const;
1313

14-
export type VSCodeAIModels = `${string}:${string}`;
1514
type VSCodeAIModel = AIModel<typeof provider.id> & { vendor: string; selector: LanguageModelChatSelector };
1615
export function isVSCodeAIModel(model: AIModel): model is AIModel<typeof provider.id> {
1716
return model.provider.id === provider.id;

src/annotations/annotationProvider.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import type { TextEditor, TextEditorDecorationType, TextEditorSelectionChangeEvent } from 'vscode';
22
import { Disposable, window } from 'vscode';
33
import type { FileAnnotationType } from '../config';
4+
import type { AnnotationStatus } from '../constants';
45
import type { Container } from '../container';
56
import { Logger } from '../system/logger';
67
import type { Deferred } from '../system/promise';
78
import { defer } from '../system/promise';
89
import type { TrackedGitDocument } from '../trackers/trackedDocument';
910
import type { Decoration } from './annotations';
1011

11-
export type AnnotationStatus = 'computing' | 'computed';
12-
1312
export interface AnnotationContext {
1413
selection?: { sha?: string; line?: never } | { sha?: never; line?: number } | false;
1514
}

src/annotations/autolinks.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ import type { ConfigurationChangeEvent } from 'vscode';
22
import { Disposable } from 'vscode';
33
import type { AutolinkReference, AutolinkType } from '../config';
44
import { GlyphChars } from '../constants';
5+
import type { IntegrationId } from '../constants.integrations';
6+
import { IssueIntegrationId } from '../constants.integrations';
57
import type { Container } from '../container';
68
import type { IssueOrPullRequest } from '../git/models/issue';
79
import { getIssueOrPullRequestHtmlIcon, getIssueOrPullRequestMarkdownIcon } from '../git/models/issue';
810
import type { GitRemote } from '../git/models/remote';
911
import type { ProviderReference } from '../git/models/remoteProvider';
1012
import type { ResourceDescriptor } from '../plus/integrations/integration';
11-
import type { IntegrationId } from '../plus/integrations/providers/models';
12-
import { IssueIntegrationId } from '../plus/integrations/providers/models';
1313
import { fromNow } from '../system/date';
1414
import { debug } from '../system/decorators/log';
1515
import { encodeUrl } from '../system/encoding';

0 commit comments

Comments
 (0)