Skip to content

Commit

Permalink
Generates telemetry docs
Browse files Browse the repository at this point in the history
 - Adds a new script to generate telemetry docs
 - Avoids imports (other than constants) in constant files (mostly)
  • Loading branch information
eamodio committed Sep 25, 2024
1 parent 76935bd commit 29c83f8
Show file tree
Hide file tree
Showing 63 changed files with 1,332 additions and 259 deletions.
906 changes: 906 additions & 0 deletions docs/telemetry-events.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17866,6 +17866,7 @@
"build:webviews": "webpack --mode development --config-name webviews",
"build:icons": "pnpm run icons:svgo && pnpm run fantasticon && pnpm run icons:apply",
"build:tests": "node ./scripts/esbuild.tests.mjs --mode development",
"generate:docs:telemetry": "node ./scripts/generate-telemetry-docs.mjs",
"bundle": "webpack --mode production",
"bundle:extension": "webpack --mode production --config-name extension:node",
"clean": "pnpx rimraf --glob dist out .vscode-test .vscode-test-web .eslintcache* tsconfig*.tsbuildinfo",
Expand Down
181 changes: 181 additions & 0 deletions scripts/generate-telemetry-docs.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
// @ts-check
import * as ts from 'typescript';
import * as fs from 'fs';
import * as path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.join(path.dirname(__filename), '..');

const filePath = path.join(__dirname, 'src/constants.telemetry.ts');

const program = ts.createProgram([filePath], {});
const sourceFile = program.getSourceFile(filePath);
const typeChecker = program.getTypeChecker();

if (!sourceFile) {
throw new Error(`Could not find source file: ${filePath}`);
}

let telemetryEventsType;
let telemetryGlobalContext;

// Find the types
ts.forEachChild(sourceFile, node => {
if (ts.isTypeAliasDeclaration(node)) {
switch (node.name.text) {
case 'TelemetryEvents':
telemetryEventsType = typeChecker.getTypeAtLocation(node);
break;
case 'TelemetryGlobalContext':
telemetryGlobalContext = typeChecker.getTypeAtLocation(node);
break;
}
}
});

if (!telemetryEventsType || !telemetryGlobalContext) {
throw new Error('Could not find the telemetry types');
}

// Generate markdown
let markdown = '# GitLens Telemetry\n\n';
markdown += '> This is a generated file. Do not edit.\n\n';

markdown += '## Global Attributes\n\n';
markdown += '> Global attributes are sent (if available) with every telemetry event\n\n';

markdown += `${expandType(telemetryGlobalContext, '', true, 'global.')}\n\n`;

markdown += '## Events\n\n';

const properties = typeChecker.getPropertiesOfType(telemetryEventsType);
for (const prop of properties) {
const propType = typeChecker.getTypeOfSymbolAtLocation(prop, sourceFile);

markdown += `### ${prop.name}\n\n`;

// Add property documentation if available
const propDocs = prop.getDocumentationComment(typeChecker);
if (propDocs.length > 0) {
markdown += `> ${propDocs.map(doc => doc.text).join('\n> ')}\n\n`;
}

// Check for deprecated tag
const jsDocTags = getJSDocTags(prop);
if (jsDocTags.deprecated) {
markdown += `> **Deprecated:** ${
jsDocTags.deprecated === true ? 'This property is deprecated.' : jsDocTags.deprecated
}\n\n`;
}

markdown += `${expandType(propType, '')}\n\n`;
}

const outputPath = path.join(__dirname, 'docs/telemetry-events.md');
fs.writeFileSync(outputPath, markdown);

function expandType(type, indent = '', isRoot = true, prefix = '') {
let result = '';

if (type.isUnion()) {
if (isRoot) {
return type.types
.map(t => `\`\`\`typescript\n${expandType(t, '', false, prefix)}\n\`\`\``)
.join('\n\nor\n\n');
} else {
const types = type.types.map(t => expandType(t, indent, false, prefix)).join(' | ');
result = types.includes('\n') ? `(${types})` : types;
}
} else if (type.isIntersection()) {
const combinedProperties = new Map();
type.types.forEach(t => {
if (t.symbol && t.symbol.flags & ts.SymbolFlags.TypeLiteral) {
typeChecker.getPropertiesOfType(t).forEach(prop => {
combinedProperties.set(prop.name, prop);
});
}
});

if (combinedProperties.size > 0) {
const expandedProps = Array.from(combinedProperties).map(([name, prop]) => {
const propType = typeChecker.getTypeOfSymbolAtLocation(prop, sourceFile);
const jsDocTags = getJSDocTags(prop);
let propString = '';
if (jsDocTags.deprecated) {
propString += `${indent} // @deprecated: ${
jsDocTags.deprecated === true ? '' : jsDocTags.deprecated
}\n`;
}
propString += `${indent} '${prefix}${name}': ${expandType(propType, indent + ' ', false, prefix)}`;
return propString;
});
result = `{\n${expandedProps.join(',\n')}\n${indent}}`;
} else {
const types = type.types.map(t => expandType(t, indent, false, prefix)).join(' & ');
result = types.includes('\n') ? `(${types})` : types;
}
} else if (type.isStringLiteral()) {
result = `'${type.value}'`;
} else if (type.isNumberLiteral()) {
result = type.value.toString();
} else if (type.symbol && type.symbol.flags & ts.SymbolFlags.Method) {
const signatures = type.getCallSignatures();
if (signatures.length) {
const params = signatures[0]
.getParameters()
.map(
p =>
`'${prefix}${p.name}': ${expandType(
typeChecker.getTypeOfSymbolAtLocation(p, sourceFile),
indent,
false,
prefix,
)}`,
)
.join(', ');
const returnType = expandType(signatures[0].getReturnType(), indent, false, prefix);
result = `(${params}) => ${returnType}`;
}
} else if (type.symbol && type.symbol.flags & ts.SymbolFlags.TypeLiteral) {
const properties = typeChecker.getPropertiesOfType(type);
if (properties.length === 0) {
result = '{}';
} else {
const expandedProps = properties.map(prop => {
const propType = typeChecker.getTypeOfSymbolAtLocation(prop, sourceFile);
const jsDocTags = getJSDocTags(prop);
let propString = '';
if (jsDocTags.deprecated) {
propString += `${indent} // @deprecated: ${
jsDocTags.deprecated === true ? '' : jsDocTags.deprecated
}\n`;
}
propString += `${indent} '${prefix}${prop.name}': ${expandType(
propType,
indent + ' ',
false,
prefix,
)}`;
return propString;
});
result = `{\n${expandedProps.join(',\n')}\n${indent}}`;
}
} else {
result = typeChecker.typeToString(type);
}

if (isRoot && !type.isUnion()) {
return `\`\`\`typescript\n${result}\n\`\`\``;
}
return result;
}

function getJSDocTags(symbol) {
const tags = {};
const jsDocTags = symbol.getJsDocTags();
for (const tag of jsDocTags) {
tags[tag.name] = tag.text ? tag.text.map(t => t.text).join(' ') : true;
}
return tags;
}
3 changes: 1 addition & 2 deletions src/ai/aiProviderService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { CancellationToken, Disposable, MessageItem, ProgressOptions, QuickInputButton } from 'vscode';
import { env, ThemeIcon, Uri, window } from 'vscode';
import type { AIModels, AIProviders, SupportedAIModels } from '../constants.ai';
import type { AIModels, AIProviders, SupportedAIModels, VSCodeAIModels } from '../constants.ai';
import type { AIGenerateDraftEventData, Sources, TelemetryEvents } from '../constants.telemetry';
import type { Container } from '../container';
import { CancellationError } from '../errors';
Expand All @@ -19,7 +19,6 @@ import type { TelemetryService } from '../telemetry/telemetry';
import { AnthropicProvider } from './anthropicProvider';
import { GeminiProvider } from './geminiProvider';
import { OpenAIProvider } from './openaiProvider';
import type { VSCodeAIModels } from './vscodeProvider';
import { isVSCodeAIModel, VSCodeAIProvider } from './vscodeProvider';

export interface AIModel<
Expand Down
10 changes: 1 addition & 9 deletions src/ai/anthropicProvider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { fetch } from '@env/fetch';
import type { CancellationToken } from 'vscode';
import { window } from 'vscode';
import type { AnthropicModels } from '../constants.ai';
import type { TelemetryEvents } from '../constants.telemetry';
import type { Container } from '../container';
import { CancellationError } from '../errors';
Expand All @@ -25,15 +26,6 @@ function isSupportedModel(model: AnthropicModel): model is SupportedModel {
return !isLegacyModel(model);
}

export type AnthropicModels =
| 'claude-instant-1'
| 'claude-2'
| 'claude-2.1'
| 'claude-3-opus-20240229'
| 'claude-3-sonnet-20240229'
| 'claude-3-5-sonnet-20240620'
| 'claude-3-haiku-20240307';

type AnthropicModel = AIModel<typeof provider.id>;

const models: AnthropicModel[] = [
Expand Down
2 changes: 1 addition & 1 deletion src/ai/geminiProvider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { fetch } from '@env/fetch';
import type { CancellationToken } from 'vscode';
import { window } from 'vscode';
import type { GeminiModels } from '../constants.ai';
import type { TelemetryEvents } from '../constants.telemetry';
import type { Container } from '../container';
import { CancellationError } from '../errors';
Expand All @@ -13,7 +14,6 @@ import { cloudPatchMessageSystemPrompt, codeSuggestMessageSystemPrompt, commitMe

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

export type GeminiModels = 'gemini-1.0-pro' | 'gemini-1.5-pro-latest' | 'gemini-1.5-flash-latest';
type GeminiModel = AIModel<typeof provider.id>;
const models: GeminiModel[] = [
{
Expand Down
18 changes: 1 addition & 17 deletions src/ai/openaiProvider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { fetch } from '@env/fetch';
import type { CancellationToken } from 'vscode';
import { window } from 'vscode';
import type { OpenAIModels } from '../constants.ai';
import type { TelemetryEvents } from '../constants.telemetry';
import type { Container } from '../container';
import { CancellationError } from '../errors';
Expand All @@ -13,23 +14,6 @@ import { cloudPatchMessageSystemPrompt, codeSuggestMessageSystemPrompt, commitMe

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

export type OpenAIModels =
| 'gpt-4o'
| 'gpt-4o-mini'
| 'gpt-4-turbo'
| 'gpt-4-turbo-2024-04-09'
| 'gpt-4-turbo-preview'
| 'gpt-4-0125-preview'
| 'gpt-4-1106-preview'
| 'gpt-4'
| 'gpt-4-0613'
| 'gpt-4-32k'
| 'gpt-4-32k-0613'
| 'gpt-3.5-turbo'
| 'gpt-3.5-turbo-0125'
| 'gpt-3.5-turbo-1106'
| 'gpt-3.5-turbo-16k';

type OpenAIModel = AIModel<typeof provider.id>;
const models: OpenAIModel[] = [
{
Expand Down
1 change: 0 additions & 1 deletion src/ai/vscodeProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { cloudPatchMessageSystemPrompt, codeSuggestMessageSystemPrompt, commitMe

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

export type VSCodeAIModels = `${string}:${string}`;
type VSCodeAIModel = AIModel<typeof provider.id> & { vendor: string; selector: LanguageModelChatSelector };
export function isVSCodeAIModel(model: AIModel): model is AIModel<typeof provider.id> {
return model.provider.id === provider.id;
Expand Down
3 changes: 1 addition & 2 deletions src/annotations/annotationProvider.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import type { TextEditor, TextEditorDecorationType, TextEditorSelectionChangeEvent } from 'vscode';
import { Disposable, window } from 'vscode';
import type { FileAnnotationType } from '../config';
import type { AnnotationStatus } from '../constants';
import type { Container } from '../container';
import { Logger } from '../system/logger';
import type { Deferred } from '../system/promise';
import { defer } from '../system/promise';
import type { TrackedGitDocument } from '../trackers/trackedDocument';
import type { Decoration } from './annotations';

export type AnnotationStatus = 'computing' | 'computed';

export interface AnnotationContext {
selection?: { sha?: string; line?: never } | { sha?: never; line?: number } | false;
}
Expand Down
4 changes: 2 additions & 2 deletions src/annotations/autolinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import type { ConfigurationChangeEvent } from 'vscode';
import { Disposable } from 'vscode';
import type { AutolinkReference, AutolinkType } from '../config';
import { GlyphChars } from '../constants';
import type { IntegrationId } from '../constants.integrations';
import { IssueIntegrationId } from '../constants.integrations';
import type { Container } from '../container';
import type { IssueOrPullRequest } from '../git/models/issue';
import { getIssueOrPullRequestHtmlIcon, getIssueOrPullRequestMarkdownIcon } from '../git/models/issue';
import type { GitRemote } from '../git/models/remote';
import type { ProviderReference } from '../git/models/remoteProvider';
import type { ResourceDescriptor } from '../plus/integrations/integration';
import type { IntegrationId } from '../plus/integrations/providers/models';
import { IssueIntegrationId } from '../plus/integrations/providers/models';
import { fromNow } from '../system/date';
import { debug } from '../system/decorators/log';
import { encodeUrl } from '../system/encoding';
Expand Down
8 changes: 2 additions & 6 deletions src/annotations/fileAnnotationController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
workspace,
} from 'vscode';
import type { AnnotationsToggleMode, FileAnnotationType } from '../config';
import type { AnnotationStatus } from '../constants';
import type { Colors, CoreColors } from '../constants.colors';
import type { Container } from '../container';
import { debug, log } from '../system/decorators/log';
Expand All @@ -40,12 +41,7 @@ import type {
DocumentDirtyIdleTriggerEvent,
DocumentDirtyStateChangeEvent,
} from '../trackers/documentTracker';
import type {
AnnotationContext,
AnnotationProviderBase,
AnnotationStatus,
TextEditorCorrelationKey,
} from './annotationProvider';
import type { AnnotationContext, AnnotationProviderBase, TextEditorCorrelationKey } from './annotationProvider';
import { getEditorCorrelationKey } from './annotationProvider';
import type { ChangesAnnotationContext } from './gutterChangesAnnotationProvider';

Expand Down
2 changes: 1 addition & 1 deletion src/commands/cloudIntegrations.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Commands } from '../constants.commands';
import type { SupportedCloudIntegrationIds } from '../constants.integrations';
import type { Source } from '../constants.telemetry';
import type { Container } from '../container';
import type { SupportedCloudIntegrationIds } from '../plus/integrations/authentication/models';
import { command } from '../system/vscode/command';
import { Command } from './base';

Expand Down
2 changes: 1 addition & 1 deletion src/commands/patches.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { env, Uri, window, workspace } from 'vscode';
import type { ScmResource } from '../@types/vscode.git.resources';
import { ScmResourceGroupType } from '../@types/vscode.git.resources.enums';
import { Commands } from '../constants.commands';
import type { IntegrationId } from '../constants.integrations';
import type { Container } from '../container';
import { CancellationError } from '../errors';
import { ApplyPatchCommitError, ApplyPatchCommitErrorReason } from '../git/errors';
Expand All @@ -15,7 +16,6 @@ import { splitGitCommitMessage } from '../git/utils/commit-utils';
import type { Draft, LocalDraft } from '../gk/models/drafts';
import { showPatchesView } from '../plus/drafts/actions';
import type { ProviderAuth } from '../plus/drafts/draftsService';
import type { IntegrationId } from '../plus/integrations/providers/models';
import { getProviderIdFromEntityIdentifier } from '../plus/integrations/providers/utils';
import type { Change, CreateDraft } from '../plus/webviews/patchDetails/protocol';
import { getRepositoryOrShowPicker } from '../quickpicks/repositoryPicker';
Expand Down
3 changes: 1 addition & 2 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { VSCodeAIModels } from './ai/vscodeProvider';
import type { SupportedAIModels } from './constants.ai';
import type { SupportedAIModels, VSCodeAIModels } from './constants.ai';
import type { ResourceDescriptor } from './plus/integrations/integration';
import type { DateTimeFormat } from './system/date';
import type { LogLevel } from './system/logger.constants';
Expand Down
Loading

0 comments on commit 29c83f8

Please sign in to comment.