Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

onAugment + hover HTTP route proof-of-concept #2116

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/standard-library/built-in-decorators.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,25 @@ nextLink: string;
```


### `@ideDoc` {#@ideDoc}

Attach additional documentation to be shown in the IDE

```typespec
@ideDoc(doc: valueof string)
```

#### Target

`(intrinsic) unknown`

#### Parameters
| Name | Type | Description |
|------|------|-------------|
| doc | `valueof scalar string` | Documentation string |



### `@inspectType` {#@inspectType}

A debugging decorator used to inspect a type.
Expand Down
6 changes: 6 additions & 0 deletions packages/compiler/lib/decorators.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ extern dec summary(target: unknown, summary: valueof string);
*/
extern dec doc(target: unknown, doc: valueof string, formatArgs?: {});

/**
* Attach additional documentation to be shown in the IDE
* @param doc Documentation string
*/
extern dec ideDoc(target: unknown, doc: valueof string);

/**
* Mark this type as deprecated
* @param message Deprecation message.
Expand Down
16 changes: 10 additions & 6 deletions packages/compiler/src/core/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,16 @@ export function createBinder(program: Program): Binder {
if (isFunctionName(key)) {
name = getFunctionName(key);
kind = "decorator";
if (name === "onValidate") {
program.onValidate(member as any);
continue;
} else if (name === "onEmit") {
// nothing to do here this is loaded as emitter.
continue;
switch (name) {
case "onValidate":
program.onValidate(member as any);
continue;
case "onAugment":
program.onAugment(member as any);
continue;
case "onEmit":
// nothing to do here this is loaded as emitter.
continue;
}
} else {
name = key;
Expand Down
60 changes: 42 additions & 18 deletions packages/compiler/src/core/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { getDirectoryPath, joinPaths, resolvePath } from "./path-utils.js";
import { createProjector } from "./projector.js";
import {
CompilerHost,
DecoratorContext,
Diagnostic,
DiagnosticTarget,
Directive,
Expand Down Expand Up @@ -88,6 +89,7 @@ export interface Program {
readonly diagnostics: readonly Diagnostic[];
loadTypeSpecScript(typespecScript: SourceFile): Promise<TypeSpecScriptNode>;
onValidate(cb: (program: Program) => void | Promise<void>): void;
onAugment(cb: (program: Program) => void | Promise<void>): void;
getOption(key: string): string | undefined;
stateSet(key: symbol): Set<Type>;
stateSets: Map<symbol, StateSet>;
Expand Down Expand Up @@ -258,7 +260,8 @@ export async function compile(
options: CompilerOptions = {},
oldProgram?: Program // NOTE: deliberately separate from options to avoid memory leak by chaining all old programs together.
): Promise<Program> {
const validateCbs: any = [];
const validateCbs: any[] = [];
const augmentCbs: any[] = [];
const stateMaps = new Map<symbol, StateMap>();
const stateSets = new Map<symbol, StateSet>();
const diagnostics: Diagnostic[] = [];
Expand Down Expand Up @@ -299,6 +302,9 @@ export async function compile(
onValidate(cb) {
validateCbs.push(cb);
},
onAugment(cb) {
augmentCbs.push(cb);
},
getGlobalNamespaceType,
resolveTypeReference,
getSourceFileLocationContext,
Expand Down Expand Up @@ -361,23 +367,16 @@ export async function compile(
if (program.hasError()) {
return program;
}
for (const cb of validateCbs) {
try {
await cb(program);
} catch (error: any) {
if (options.designTimeBuild) {
program.reportDiagnostic(
createDiagnostic({
code: "on-validate-fail",
format: { error: error.stack },
target: NoTarget,
})
);
} else {
throw error;
}
}
}

const onAugmentDecoratorContext: DecoratorContext = {
program,
decoratorTarget: NoTarget,
call: (decorator, target, ...args) => decorator(onAugmentDecoratorContext, target, ...args),
getArgumentTarget: () => undefined,
};

await runCallbacks(augmentCbs, program, options, onAugmentDecoratorContext);
await runCallbacks(validateCbs, program, options);

for (const [requiredImport, emitterName] of requireImports) {
if (!loadedLibraries.has(requiredImport)) {
Expand Down Expand Up @@ -1146,6 +1145,31 @@ export async function compile(
}
}

async function runCallbacks(
callbacks: any[],
program: Program,
options: CompilerOptions,
arg: Program | DecoratorContext = program
) {
for (const cb of callbacks) {
try {
await cb(arg);
} catch (error: any) {
if (options.designTimeBuild) {
program.reportDiagnostic(
createDiagnostic({
code: "on-validate-fail",
format: { error: error.stack },
target: NoTarget,
})
);
} else {
throw error;
}
}
}
}

export function createStateAccessors(
stateMaps: Map<symbol, StateMap>,
stateSets: Map<symbol, StateSet>,
Expand Down
2 changes: 1 addition & 1 deletion packages/compiler/src/core/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2016,7 +2016,7 @@ export interface DecoratorContext {
/**
* Point to the decorator target
*/
decoratorTarget: DiagnosticTarget;
decoratorTarget: DiagnosticTarget | typeof NoTarget;

/**
* Function that can be used to retrieve the target for a parameter at the given index.
Expand Down
14 changes: 14 additions & 0 deletions packages/compiler/src/lib/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,20 @@ export function $doc(context: DecoratorContext, target: Type, text: string, sour
setDocData(context.program, target, { value: text, source: "@doc" });
}

const ideDocsKey = createStateSymbol("ideDocs");
export function $ideDoc(context: DecoratorContext, target: Type, text: string) {
const ideDocs: string[] = context.program.stateMap(ideDocsKey).get(target);
if (ideDocs) {
ideDocs.push(text);
} else {
context.program.stateMap(ideDocsKey).set(target, [text]);
}
}

export function getIdeDocs(program: Program, target: Type): string[] {
return program.stateMap(ideDocsKey).get(target) ?? [];
}

/**
* @internal to be used to set the `@doc` from doc comment.
*/
Expand Down
5 changes: 4 additions & 1 deletion packages/compiler/src/server/type-details.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
TemplateDeclarationNode,
Type,
} from "../core/index.js";
import { getDocData } from "../lib/decorators.js";
import { getDocData, getIdeDocs } from "../lib/decorators.js";
import { getSymbolSignature } from "./type-signature.js";

/**
Expand Down Expand Up @@ -69,6 +69,9 @@ function getSymbolDocumentation(program: Program, symbol: Sym) {
docs.push(apiDocs.value);
}

// Add @ideDoc(...) docs
docs.push(...getIdeDocs(program, type));

return docs.join("\n\n");
}

Expand Down
19 changes: 19 additions & 0 deletions packages/http/src/augment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { $ideDoc, DecoratorContext, ignoreDiagnostics } from "@typespec/compiler";
import { getAllHttpServices } from "./operations.js";

export function $onAugment(context: DecoratorContext) {
if (!context.program.compilerOptions.designTimeBuild) {
// we only add IDE docs, so don't do this in non-IDE builds
return;
}
const services = ignoreDiagnostics(getAllHttpServices(context.program));
for (const service of services) {
for (const operation of service.operations) {
context.call(
$ideDoc,
operation.operation,
`${operation.verb.toUpperCase()} ${operation.path}`
);
}
}
}
1 change: 1 addition & 0 deletions packages/http/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export const namespace = "TypeSpec.Http";

export * from "./augment.js";
export * from "./content-types.js";
export * from "./decorators.js";
export * from "./metadata.js";
Expand Down
Loading