From f0cfd8ade2a60851af4947f32926d4e365ff1953 Mon Sep 17 00:00:00 2001 From: angelozerr Date: Sat, 24 Apr 2021 13:46:16 +0200 Subject: [PATCH] Provide documentation on hover, in .kafka files Fixes #149 Signed-off-by: azerr --- CHANGELOG.md | 1 + docs/Consuming.md | 7 + docs/Producing.md | 8 + .../kafka-file-consumer-topic-hover.png | Bin 0 -> 10690 bytes .../kafka-file-producer-topic-hover.png | Bin 0 -> 13362 bytes src/docs/markdownPreviewProvider.ts | 14 +- src/kafka-file/kafkaFileClient.ts | 18 +- .../kafkaFileLanguageService.ts | 23 +- src/kafka-file/languageservice/model.ts | 32 +- .../languageservice/parser/kafkaFileParser.ts | 39 ++- .../languageservice/services/completion.ts | 21 +- .../languageservice/services/diagnostics.ts | 6 +- .../languageservice/services/hover.ts | 170 ++++++++++ .../kafka-file/languageservice/hover.test.ts | 310 ++++++++++++++++++ .../kafka-file/languageservice/kafkaAssert.ts | 37 ++- 15 files changed, 642 insertions(+), 44 deletions(-) create mode 100644 docs/assets/kafka-file-consumer-topic-hover.png create mode 100644 docs/assets/kafka-file-producer-topic-hover.png create mode 100644 src/kafka-file/languageservice/services/hover.ts create mode 100644 src/test/suite/kafka-file/languageservice/hover.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index b043ebd6..d9e3cfcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to `Tools for Apache Kafka®` are documented in this file. - Hide internal [strimzi](https://strimzi.io/) topics/consumers by default. See [#176](https://github.com/jlandersen/vscode-kafka/pull/176). - Validation for available topics in `.kafka` files. See [#153](https://github.com/jlandersen/vscode-kafka/issues/153). - Simplify snippets. See [#180](https://github.com/jlandersen/vscode-kafka/pull/180). +- Hover support in `.kafka` files. See [#149](https://github.com/jlandersen/vscode-kafka/issues/149). ## [0.12.0] - 2021-04-26 ### Added diff --git a/docs/Consuming.md b/docs/Consuming.md index 8be5cbc9..892da260 100644 --- a/docs/Consuming.md +++ b/docs/Consuming.md @@ -101,6 +101,13 @@ Existing topic validation is done only when cluster is `connected`. If the topic ![Existing topic validation](assets/kafka-file-consumer-topic-validation.png) +#### Hover + +Hover for properties documentation and topic informations is available in .kafka files. + +Here is an example of hover on topic: + +![Existing topic validation](assets/kafka-file-consumer-topic-hover.png) ### Start Consumer command diff --git a/docs/Producing.md b/docs/Producing.md index 3634b8f6..a865224d 100644 --- a/docs/Producing.md +++ b/docs/Producing.md @@ -83,6 +83,14 @@ Validation will help you write valid producers in .kafka files. ![Existing topic validation](assets/kafka-file-producer-topic-validation.png) +### Hover + +Hover for properties documentation and topic informations is available in .kafka files. + +Here is an example of hover on topic: + +![Existing topic validation](assets/kafka-file-producer-topic-hover.png) + ## Randomized content Record content can be randomized by injecting mustache-like placeholders of [faker.js properties](https://github.com/Marak/faker.js#api-methods), like ``{{name.lastName}}`` or ``{{random.number}}``. Some randomized properties can be localized via the `kafka.producers.fakerjs.locale` setting. diff --git a/docs/assets/kafka-file-consumer-topic-hover.png b/docs/assets/kafka-file-consumer-topic-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..c4cf482334b13b4beed5177d036f3cbf0204fb0b GIT binary patch literal 10690 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 literal 0 HcmV?d00001 diff --git a/docs/assets/kafka-file-producer-topic-hover.png b/docs/assets/kafka-file-producer-topic-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..d564dce683f17b60f9eb550917b6762817f5299f GIT binary patch literal 13362 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 literal 0 HcmV?d00001 diff --git a/src/docs/markdownPreviewProvider.ts b/src/docs/markdownPreviewProvider.ts index 71cc61df..523cacba 100644 --- a/src/docs/markdownPreviewProvider.ts +++ b/src/docs/markdownPreviewProvider.ts @@ -62,7 +62,7 @@ class MarkdownPreviewProvider implements Disposable { return `${linkText}`; }); body = await commands.executeCommand(MARKDOWN_API_RENDER, markdownString); - if(body !== undefined) { + if (body !== undefined) { this.documentCache.set(markdownFilePath, body); } } @@ -120,3 +120,15 @@ class MarkdownPreviewProvider implements Disposable { } export const markdownPreviewProvider: MarkdownPreviewProvider = new MarkdownPreviewProvider(); + +export type EmbeddedPage = "Consuming" | "Producing"; + +type ConsumingSection = "deserializer" | "kafka-file"; + +type ProducingSection = "serializer" | "kafka-file" | "randomized-content"; + +export type EmbeddedSection = ConsumingSection | ProducingSection; + +export function getDocumentationPageUri(page: EmbeddedPage, section: EmbeddedSection) { + return `command:vscode-kafka.open.docs.page?%5B%7B%22page%22%3A%22${page}%22%2C%22section%22%3A%22${section}%22%7D%5D`; +} diff --git a/src/kafka-file/kafkaFileClient.ts b/src/kafka-file/kafkaFileClient.ts index 890e2333..39852fc0 100644 --- a/src/kafka-file/kafkaFileClient.ts +++ b/src/kafka-file/kafkaFileClient.ts @@ -164,6 +164,12 @@ export function startLanguageClient( const diagnostics = new KafkaFileDiagnostics(kafkaFileDocuments, languageService, clusterSettings, clientAccessor, modelProvider, workspaceSettings); context.subscriptions.push(diagnostics); + // Hover + const hover = new KafkaFileHoverProvider(kafkaFileDocuments, languageService); + context.subscriptions.push( + vscode.languages.registerHoverProvider(documentSelector, hover) + ); + // Open / Close document context.subscriptions.push(vscode.workspace.onDidOpenTextDocument(e => { if (e.languageId === 'kafka') { @@ -277,7 +283,7 @@ class KafkaFileCompletionItemProvider extends AbstractKafkaFileFeature implement return runSafeAsync(async () => { const kafkaFileDocument = this.getKafkaFileDocument(document); return this.languageService.doComplete(document, kafkaFileDocument, this.workspaceSettings.producerFakerJSEnabled, position); - }, new vscode.CompletionList(), `Error while computing code lenses for ${document.uri}`, token); + }, new vscode.CompletionList(), `Error while computing completion for ${document.uri}`, token); } } @@ -359,3 +365,13 @@ class KafkaFileDiagnostics extends AbstractKafkaFileFeature implements vscode.Di this.diagnosticCollection.dispose(); } } + +class KafkaFileHoverProvider extends AbstractKafkaFileFeature implements vscode.HoverProvider { + provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): vscode.ProviderResult { + return runSafeAsync(async () => { + const kafkaFileDocument = this.getKafkaFileDocument(document); + return this.languageService.doHover(document, kafkaFileDocument, position); + }, null, `Error while computing hover for ${document.uri}`, token); + } + +} \ No newline at end of file diff --git a/src/kafka-file/languageservice/kafkaFileLanguageService.ts b/src/kafka-file/languageservice/kafkaFileLanguageService.ts index e88f5535..cf27cfb4 100644 --- a/src/kafka-file/languageservice/kafkaFileLanguageService.ts +++ b/src/kafka-file/languageservice/kafkaFileLanguageService.ts @@ -1,4 +1,4 @@ -import { CodeLens, CompletionList, Diagnostic, Position, TextDocument, Uri } from "vscode"; +import { CodeLens, CompletionList, Diagnostic, Hover, Position, TextDocument, Uri } from "vscode"; import { ClientState, ConsumerLaunchState } from "../../client"; import { BrokerConfigs } from "../../client/config"; import { ProducerLaunchState } from "../../client/producer"; @@ -6,6 +6,7 @@ import { KafkaFileDocument, parseKafkaFile } from "./parser/kafkaFileParser"; import { KafkaFileCodeLenses } from "./services/codeLensProvider"; import { KafkaFileCompletion } from "./services/completion"; import { KafkaFileDiagnostics } from "./services/diagnostics"; +import { KafkaFileHover } from "./services/hover"; /** * Provider API which gets the state for a given producer. @@ -49,6 +50,7 @@ export interface TopicProvider { * */ export interface LanguageService { + /** * Parse the given text document and returns an AST. * @@ -85,6 +87,15 @@ export interface LanguageService { * @param kafkaFileDocument the parsed AST. */ doDiagnostics(document: TextDocument, kafkaFileDocument: KafkaFileDocument, producerFakerJSEnabled: boolean): Promise; + + /** + * Returns the hover result for the given text document and parsed AST at given position. + * + * @param document the text document. + * @param kafkaFileDocument the parsed AST. + * @param position the position where the hover was triggered. + */ + doHover(document: TextDocument, kafkaFileDocument: KafkaFileDocument, position: Position): Promise; } /** @@ -100,10 +111,18 @@ export function getLanguageService(producerLaunchStateProvider: ProducerLaunchSt const codeLenses = new KafkaFileCodeLenses(producerLaunchStateProvider, consumerLaunchStateProvider, selectedClusterProvider); const completion = new KafkaFileCompletion(selectedClusterProvider, topicProvider); const diagnostics = new KafkaFileDiagnostics(selectedClusterProvider, topicProvider); + const hover = new KafkaFileHover(selectedClusterProvider, topicProvider); return { parseKafkaFileDocument: (document: TextDocument) => parseKafkaFile(document), getCodeLenses: codeLenses.getCodeLenses.bind(codeLenses), doComplete: completion.doComplete.bind(completion), - doDiagnostics: diagnostics.doDiagnostics.bind(diagnostics) + doDiagnostics: diagnostics.doDiagnostics.bind(diagnostics), + doHover: hover.doHover.bind(hover) }; } + +export function createTopicDocumentation(topic: TopicDetail): string { + return `Topic \`${topic.id}\`\n` + + ` * partition count: \`${topic.partitionCount}\`\n` + + ` * replication factor: \`${topic.replicationFactor}\`\n`; +} \ No newline at end of file diff --git a/src/kafka-file/languageservice/model.ts b/src/kafka-file/languageservice/model.ts index 360fe7b1..caef9998 100644 --- a/src/kafka-file/languageservice/model.ts +++ b/src/kafka-file/languageservice/model.ts @@ -1,3 +1,5 @@ +import { getDocumentationPageUri } from "../../docs/markdownPreviewProvider"; + export class Model { private cache = new Map(); @@ -16,18 +18,22 @@ export class Model { } public hasDefinitionEnum(name: string, value: string): boolean { + return this.getDefinitionEnum(name, value) !== undefined; + } + + public getDefinitionEnum(name: string, value: string): ModelDefinition | undefined { const definition = this.getDefinition(name); if (!definition) { - return false; + return undefined; } if (definition.enum) { for (const item of definition.enum) { if (item.name === value) { - return true; + return item; } } } - return false; + return undefined; } } @@ -59,7 +65,7 @@ const consumerProperties = [ }, { name: "key-format", - description: "[Deserializer](https://github.com/jlandersen/vscode-kafka/blob/master/docs/Consuming.md#Deserializer) to use for the key *[optional]*.", + description: `[Deserializer](${getDocumentationPageUri('Consuming', 'deserializer')}) to use for the key *[optional]*.`, enum: [ { name: "none", @@ -93,7 +99,7 @@ const consumerProperties = [ }, { name: "value-format", - description: "[Deserializer](https://github.com/jlandersen/vscode-kafka/blob/master/docs/Consuming.md#Deserializer) to use for the value *[optional]*.", + description: `[Deserializer](${getDocumentationPageUri('Consuming', 'deserializer')}) to use for the value *[optional]*.`, enum: [ { name: "none", @@ -148,7 +154,7 @@ const producerProperties = [ }, { name: "key-format", - description: "[Serializer](https://github.com/jlandersen/vscode-kafka/blob/master/docs/Producing.md#Serializer) to use for the key *[optional]*.", + description: `[Serializer](${getDocumentationPageUri('Producing', 'serializer')}) to use for the key *[optional]*.`, enum: [ { name: "string", @@ -178,7 +184,7 @@ const producerProperties = [ }, { name: "value-format", - description: "[Serializer](https://github.com/jlandersen/vscode-kafka/blob/master/docs/Producing.md#Serializer) to use for the value *[optional]*.", + description: `[Deserializer](${getDocumentationPageUri('Producing', 'serializer')}) to use for the value *[optional]*.`, enum: [ { name: "string", @@ -381,18 +387,18 @@ const fakerjsAPI = [ ] as ModelDefinition[]; export interface PartModelProvider { - getPart(name: string) : PartModelProvider | undefined; + getPart(name: string): PartModelProvider | undefined; } -class PartModel implements PartModelProvider{ +class PartModel implements PartModelProvider { private cache = new Map(); - getPart(name: string) : PartModelProvider | undefined { + getPart(name: string): PartModelProvider | undefined { return this.cache.get(name); } - getOrCreate(name: string) : PartModelProvider { + getOrCreate(name: string): PartModelProvider { let part = this.getPart(name); if (!part) { part = new PartModel(); @@ -411,12 +417,12 @@ class FakerJSModel extends Model implements PartModelProvider { const parts = definition.name.split('.'); let partModel = this.root; parts.forEach(part => { - partModel = partModel.getOrCreate(part); + partModel = partModel.getOrCreate(part); }); }); } - getPart(name: string) : PartModelProvider | undefined { + getPart(name: string): PartModelProvider | undefined { return this.root.getPart(name); } } diff --git a/src/kafka-file/languageservice/parser/kafkaFileParser.ts b/src/kafka-file/languageservice/parser/kafkaFileParser.ts index b759dab1..db3fc849 100644 --- a/src/kafka-file/languageservice/parser/kafkaFileParser.ts +++ b/src/kafka-file/languageservice/parser/kafkaFileParser.ts @@ -17,7 +17,9 @@ export enum NodeKind { export interface Node { start: Position; end: Position; + range(): Range; findNodeBefore(offset: Position): Node; + findNodeAt(offset: Position): Node; lastChild: Node | undefined; parent: Node | undefined; kind: NodeKind; @@ -32,10 +34,20 @@ class BaseNode implements Node { } + public range(): Range { + const start = this.start; + const end = this.end; + return new Range(start, end); + } + public findNodeBefore(offset: Position): Node { return this; } + public findNodeAt(offset: Position): Node { + return this; + } + public get lastChild(): Node | undefined { return undefined; } } @@ -65,6 +77,17 @@ class ChildrenNode extends BaseNode { return this; } + public findNodeAt(offset: Position): Node { + const idx = findFirst(this.children, c => offset.isBeforeOrEqual(c.start)) - 1; + if (idx >= 0) { + const child = this.children[idx]; + if (offset.isAfter(child.start) && offset.isBeforeOrEqual(child.end)) { + return child.findNodeAt(offset); + } + } + return this; + } + public get lastChild(): Node | undefined { return this.children.length ? this.children[this.children.length - 1] : void 0; }; } @@ -84,6 +107,7 @@ export class Chunk extends BaseNode { constructor(public readonly content: string, start: Position, end: Position, kind: NodeKind) { super(start, end, kind); } + } export class Property extends BaseNode { @@ -106,12 +130,6 @@ export class Property extends BaseNode { return this.value?.content.trim(); } - public get propertyRange(): Range { - const start = this.start; - const end = this.end; - return new Range(start, end); - } - public get propertyKeyRange(): Range { const start = this.start; const end = this.assignerCharacter ? new Position(this.start.line, this.assignerCharacter) : this.end; @@ -159,6 +177,13 @@ export class Property extends BaseNode { } return true; } + + findNodeAt(position : Position) : Node { + if (this.isBeforeAssigner(position)) { + return this.key?.findNodeAt(position) || this; + } + return this.value?.findNodeAt(position) || this; + } } export abstract class Block extends ChildrenNode { @@ -175,7 +200,7 @@ export abstract class Block extends ChildrenNode { getPropertyValue(name: string): string | undefined { const property = this.getProperty(name); - return property?.value?.content; + return property?.propertyValue; } getProperty(name: string): Property | undefined { diff --git a/src/kafka-file/languageservice/services/completion.ts b/src/kafka-file/languageservice/services/completion.ts index 2cd2a90b..36aec62a 100644 --- a/src/kafka-file/languageservice/services/completion.ts +++ b/src/kafka-file/languageservice/services/completion.ts @@ -1,5 +1,5 @@ import { TextDocument, Position, CompletionList, CompletionItem, SnippetString, MarkdownString, CompletionItemKind, Range } from "vscode"; -import { SelectedClusterProvider, TopicDetail, TopicProvider } from "../kafkaFileLanguageService"; +import { createTopicDocumentation, SelectedClusterProvider, TopicProvider } from "../kafkaFileLanguageService"; import { consumerModel, fakerjsAPIModel, Model, ModelDefinition, producerModel } from "../model"; import { Block, BlockType, Chunk, ConsumerBlock, KafkaFileDocument, MustacheExpression, NodeKind, ProducerBlock, Property } from "../parser/kafkaFileParser"; @@ -134,7 +134,7 @@ export class KafkaFileCompletion { const item = new CompletionItem(currentName); item.kind = CompletionItemKind.Property; if (definition.description) { - item.documentation = new MarkdownString(definition.description); + item.documentation = createMarkdownString(definition.description); } const insertText = new SnippetString(`${currentName}: `); const values = await this.getValues(definition); @@ -198,7 +198,7 @@ export class KafkaFileCompletion { const item = new CompletionItem(value); item.kind = CompletionItemKind.Value; if (definition.description) { - item.documentation = new MarkdownString(definition.description); + item.documentation = createMarkdownString(definition.description); } const insertText = new SnippetString(' '); insertText.appendText(value); @@ -219,7 +219,7 @@ export class KafkaFileCompletion { const item = new CompletionItem(value); item.kind = CompletionItemKind.Variable; if (definition.description) { - item.documentation = new MarkdownString(definition.description); + item.documentation = createMarkdownString(definition.description); } const insertText = new SnippetString(''); insertText.appendText(value); @@ -235,11 +235,6 @@ export class KafkaFileCompletion { return; } - function createDocumentation(topic: TopicDetail): string { - return `Topic \`${topic.id}\`\n` + - ` * partition count: \`${topic.partitionCount}\`\n` + - ` * replication factor: \`${topic.replicationFactor}\`\n`; - } const valueRange = property.propertyValueRange; try { const topics = await this.topicProvider.getTopics(clusterId); @@ -247,7 +242,7 @@ export class KafkaFileCompletion { const value = topic.id; const item = new CompletionItem(value); item.kind = CompletionItemKind.Value; - item.documentation = new MarkdownString(createDocumentation(topic)); + item.documentation = new MarkdownString(createTopicDocumentation(topic)); const insertText = new SnippetString(' '); insertText.appendText(value); item.insertText = insertText; @@ -281,3 +276,9 @@ export class KafkaFileCompletion { } } } + +function createMarkdownString(contents : string) { + const doc = new MarkdownString(contents); + doc.isTrusted = true; + return doc; +} \ No newline at end of file diff --git a/src/kafka-file/languageservice/services/diagnostics.ts b/src/kafka-file/languageservice/services/diagnostics.ts index d05a4135..94e07152 100644 --- a/src/kafka-file/languageservice/services/diagnostics.ts +++ b/src/kafka-file/languageservice/services/diagnostics.ts @@ -208,14 +208,14 @@ export class KafkaFileDiagnostics { const assigner = property.assignerCharacter; if (!assigner) { // Error => topic - const range = property.propertyRange; + const range = property.range(); diagnostics.push(new Diagnostic(range, `Missing ':' sign after '${propertyName}'`, DiagnosticSeverity.Error)); return; } // 1.2. property must declare a key if (!propertyName) { // Error => :string - const range = property.propertyRange; + const range = property.range(); diagnostics.push(new Diagnostic(range, "Property must define a name before ':' sign", DiagnosticSeverity.Error)); return; } @@ -280,7 +280,7 @@ export class KafkaFileDiagnostics { // The topic validation is done, only when the cluster is connected if (!await this.topicProvider.getTopic(clusterId, topicId)) { // The topic doesn't exist, report an error - const range = topicProperty.propertyTrimmedValueRange || topicProperty.propertyRange; + const range = topicProperty.propertyTrimmedValueRange || topicProperty.range(); const autoCreate = await this.topicProvider.getAutoCreateTopicEnabled(clusterId); const errorMessage = getTopicErrorMessage(topicId, autoCreate, blockType); const severity = getTopicErrorSeverity(autoCreate); diff --git a/src/kafka-file/languageservice/services/hover.ts b/src/kafka-file/languageservice/services/hover.ts new file mode 100644 index 00000000..36218c71 --- /dev/null +++ b/src/kafka-file/languageservice/services/hover.ts @@ -0,0 +1,170 @@ +import { Hover, MarkdownString, Position, Range, TextDocument } from "vscode"; +import { getDocumentationPageUri } from "../../../docs/markdownPreviewProvider"; +import { createTopicDocumentation, SelectedClusterProvider, TopicProvider } from "../kafkaFileLanguageService"; +import { consumerModel, Model, producerModel } from "../model"; +import { Block, BlockType, Chunk, ConsumerBlock, KafkaFileDocument, MustacheExpression, NodeKind, ProducerBlock, Property } from "../parser/kafkaFileParser"; + +export class KafkaFileHover { + + constructor(private selectedClusterProvider: SelectedClusterProvider, private topicProvider: TopicProvider) { + + } + + async doHover(document: TextDocument, kafkaFileDocument: KafkaFileDocument, position: Position): Promise { + // Get the AST node before the position where complation was triggered + const node = kafkaFileDocument.findNodeAt(position); + if (!node) { + return; + } + switch (node.kind) { + + case NodeKind.consumerBlock: { + const block = node; + const topic = block.getPropertyValue('topic'); + return createHover(`Consumer declaration${topic ? ` for topic \`${topic}\`` : ''}.\n\nSee [here](${getDocumentationPageUri('Consuming', 'kafka-file')}) for more informations.`, node.range()); + } + + case NodeKind.producerBlock: { + const block = node; + const topic = block.getPropertyValue('topic'); + return createHover(`Producer declaration${topic ? ` for topic \`${topic}\`` : ''}.\n\nSee [here](${getDocumentationPageUri('Producing', 'kafka-file')}) for more informations.`, node.range()); + } + + case NodeKind.propertyKey: { + const propertyKey = node; + const property = propertyKey.parent; + const propertyName = propertyKey.content; + const propertyKeyRange = propertyKey.range(); + const block = property.parent; + if (block.type === BlockType.consumer) { + // CONSUMER + // key|: + + // or + + // CONSUMER + // key| + return await this.getHoverForConsumerPropertyNames(propertyName, propertyKeyRange, block); + } else { + // PRODUCER + // key|: + return await this.getHoverForProducerPropertyNames(propertyName, propertyKeyRange, block); + } + } + + case NodeKind.propertyValue: { + const propertyValue = node; + const property = propertyValue.parent; + const block = property.parent; + if (block.type === BlockType.consumer) { + // CONSUMER + // key-format: | + return await this.getHoverForConsumerPropertyValues(propertyValue, property, block); + } else { + // PRODUCER + // key-format: | + return await this.getHoverForProducerPropertyValues(propertyValue, property, block); + } + } + + case NodeKind.mustacheExpression: { + const expression = node; + return createHover(`FakerJS expression.\n\nSee [here](${getDocumentationPageUri('Producing', 'randomized-content')}) for more informations.`, expression.enclosedExpressionRange); + } + + case NodeKind.producerValue: { + return createHover(`Producer value.\n\nSee [here](${getDocumentationPageUri('Producing', 'kafka-file')}) for more informations.`, node.range()); + } + } + } + + async getHoverForConsumerPropertyNames(propertyName: string, propertyKeyRange: Range, block: ConsumerBlock): Promise { + return await this.getHoverForPropertyNames(propertyName, propertyKeyRange, block, consumerModel); + } + + async getHoverForProducerPropertyNames(propertyName: string, propertyKeyRange: Range, block: ProducerBlock): Promise { + return await this.getHoverForPropertyNames(propertyName, propertyKeyRange, block, producerModel); + } + + async getHoverForPropertyNames(propertyName: string, propertyKeyRange: Range, block: Block, metadata: Model): Promise { + const definition = metadata.getDefinition(propertyName); + if (definition && definition.description) { + return createHover(definition.description, propertyKeyRange); + } + } + + async getHoverForConsumerPropertyValues(propertyValue: Chunk, property: Property, block: ConsumerBlock): Promise { + const propertyName = property.propertyName; + switch (propertyName) { + case 'topic': + // CONSUMER + // topic: | + return await this.getHoverForTopic(property); + default: + // CONSUMER + // key-format: | + return await this.getHoverForPropertyValues(propertyValue, property, block, consumerModel); + } + } + + + async getHoverForProducerPropertyValues(propertyValue: Chunk, property: Property, block: ProducerBlock): Promise { + const propertyName = property.propertyName; + switch (propertyName) { + case 'topic': + // PRODUCER + // topic: | + return await this.getHoverForTopic(property); + default: + // PRODUCER + // key-format: | + return await this.getHoverForPropertyValues(propertyValue, property, block, producerModel); + } + } + + async getHoverForTopic(property: Property): Promise { + const propertyValue = property.value; + if (!propertyValue) { + return; + } + const { clusterId } = this.selectedClusterProvider.getSelectedCluster(); + if (!clusterId) { + return; + } + + try { + const topicId = propertyValue.content.trim(); + const topics = await this.topicProvider.getTopics(clusterId); + if (topics.length > 0) { + const topic = topics + .find(t => t.id === topicId); + if (topic) { + return createHover(createTopicDocumentation(topic), propertyValue.range()); + } + } + } + catch (e) { + return; + } + + return undefined; + } + + async getHoverForPropertyValues(propertyValue: Chunk, property: Property, block: Block, metadata: Model): Promise { + const propertyName = property.propertyName; + if (!propertyName) { + return; + } + const definition = metadata.getDefinitionEnum(propertyName, propertyValue.content.trim()); + if (definition && definition.description) { + return createHover(definition.description, property.propertyTrimmedValueRange); + } + return undefined; + } +} + +function createHover(contents: string, range?: Range): Hover { + const doc = new MarkdownString(contents); + doc.isTrusted = true; + return new Hover(doc, range); +} \ No newline at end of file diff --git a/src/test/suite/kafka-file/languageservice/hover.test.ts b/src/test/suite/kafka-file/languageservice/hover.test.ts new file mode 100644 index 00000000..8f0bd10e --- /dev/null +++ b/src/test/suite/kafka-file/languageservice/hover.test.ts @@ -0,0 +1,310 @@ +import { ClientState } from "../../../../client"; +import { getLanguageService } from "../../../../kafka-file/languageservice/kafkaFileLanguageService"; +import { assertHover, hover, LanguageServiceConfig, position } from "./kafkaAssert"; + +suite("Kafka File Hover Test Suite", () => { + + test("Empty hover", async () => { + await assertHover(''); + + await assertHover('ab|cd'); + + }); + +}); + +suite("Kafka File CONSUMER Hover Test Suite", () => { + + test("CONSUMER declaration no topic Hover", async () => { + + await assertHover( + 'CONS|UMER\n', + hover( + `Consumer declaration.\n\nSee [here](command:vscode-kafka.open.docs.page?%5B%7B%22page%22%3A%22Consuming%22%2C%22section%22%3A%22kafka-file%22%7D%5D) for more informations.`, + position(0, 0), + position(1, 0) + ) + ); + + }); + + test("CONSUMER declaration with topic Hover", async () => { + + await assertHover( + 'CONS|UMER\n' + + 'topic: abcd', + hover( + `Consumer declaration for topic \`abcd\`.\n\n\See [here](command:vscode-kafka.open.docs.page?%5B%7B%22page%22%3A%22Consuming%22%2C%22section%22%3A%22kafka-file%22%7D%5D) for more informations.`, + position(0, 0), + position(1, 11) + ) + ); + + }); + + test("topic property name Hover", async () => { + + await assertHover( + 'CONSUMER\n' + + 'top|ic: abcd', + hover( + `The topic id *[required]*`, + position(1, 0), + position(1, 5) + ) + ); + + }); + + test("topic property value Hover", async () => { + + await assertHover( + 'CONSUMER\n' + + 'topic: ab|cd' + ); + + const languageServiceConfig = new LanguageServiceConfig(); + languageServiceConfig.setTopics('cluster1', [{ id: 'abcd', partitionCount: 1, replicationFactor: 1 }]); + const connectedCuster = { clusterId: 'cluster1', clusterName: 'CLUSTER_1', clusterState: ClientState.connected }; + languageServiceConfig.setSelectedCluster(connectedCuster); + const languageService = getLanguageService(languageServiceConfig, languageServiceConfig, languageServiceConfig, languageServiceConfig); + + await assertHover( + 'CONSUMER\n' + + 'topic: ab|cd', + hover( + 'Topic `abcd`\n * partition count: `1`\n * replication factor: `1`\n', + position(1, 6), + position(1, 11) + ), + languageService + ); + + }); + + test("from property name Hover", async () => { + + await assertHover( + 'CONSUMER\n' + + 'fro|m: earliest', + hover( + 'The offset from which the consumer group will start consuming messages from. Possible values are: `earliest`, `latest`, or an integer value. *[optional]*.', + position(1, 0), + position(1, 4) + ) + ); + + }); + + test("key-format property name Hover", async () => { + + await assertHover( + 'CONSUMER\n' + + 'key-for|mat: string', + hover( + '[Deserializer](command:vscode-kafka.open.docs.page?%5B%7B%22page%22%3A%22Consuming%22%2C%22section%22%3A%22deserializer%22%7D%5D) to use for the key *[optional]*.', + position(1, 0), + position(1, 10) + ) + ); + + }); + + test("key-format property value Hover", async () => { + + await assertHover( + 'CONSUMER\n' + + 'key-format: stri|ng', + hover( + 'Similar deserializer to the Kafka Java client [org.apache.kafka.common.serialization.StringDeserializer](https://github.com/apache/kafka/blob/master/clients/src/main/java/org/apache/kafka/common/serialization/StringDeserializer.java) which currently only supports `UTF-8` encoding.', + position(1, 12), + position(1, 18) + ) + ); + + }); + + test("value-format property name Hover", async () => { + + await assertHover( + 'CONSUMER\n' + + 'value-for|mat: string', + hover( + '[Deserializer](command:vscode-kafka.open.docs.page?%5B%7B%22page%22%3A%22Consuming%22%2C%22section%22%3A%22deserializer%22%7D%5D) to use for the value *[optional]*.', + position(1, 0), + position(1, 12) + ) + ); + + }); + + test("value-format property value Hover", async () => { + + await assertHover( + 'CONSUMER\n' + + 'value-format: stri|ng', + hover( + 'Similar deserializer to the Kafka Java client [org.apache.kafka.common.serialization.StringDeserializer](https://github.com/apache/kafka/blob/master/clients/src/main/java/org/apache/kafka/common/serialization/StringDeserializer.java) which currently only supports `UTF-8` encoding.', + position(1, 14), + position(1, 20) + ) + ); + + }); + + test("partitions property name Hover", async () => { + + await assertHover( + 'CONSUMER\n' + + 'partition|s: 0', + hover( + 'the partition number(s), or a partitions range, or a combinaison of partitions ranges *[optional]*. eg:\n* 0\n* 0,1,2\n* 0-2\n* 0,2-3', + position(1, 0), + position(1, 10) + ) + ); + + }); + +}); + +suite("Kafka File PRODUCER Hover Test Suite", () => { + + test("PRODUCER declaration no topic Hover", async () => { + + await assertHover( + 'PRODU|CER\n', + hover( + `See [here](command:vscode-kafka.open.docs.page?%5B%7B%22page%22%3A%22Producing%22%2C%22section%22%3A%22kafka-file%22%7D%5D) for more informations.`, + position(0, 0), + position(1, 0) + ) + ); + + }); + + test("PRODUCER declaration with topic Hover", async () => { + + await assertHover( + 'PRODU|CER\n' + + 'topic: abcd', + hover( + `Producer declaration for topic \`abcd\`.\n\n\See [here](command:vscode-kafka.open.docs.page?%5B%7B%22page%22%3A%22Producing%22%2C%22section%22%3A%22kafka-file%22%7D%5D) for more informations.`, + position(0, 0), + position(1, 11) + ) + ); + + test("topic property name Hover", async () => { + + await assertHover( + 'PRODUCER\n' + + 'top|ic: abcd', + hover( + `The topic id *[required]*`, + position(1, 0), + position(1, 5) + ) + ); + + }); + + test("topic property value Hover", async () => { + + await assertHover( + 'PRODUCER\n' + + 'topic: ab|cd' + ); + + const languageServiceConfig = new LanguageServiceConfig(); + languageServiceConfig.setTopics('cluster1', [{ id: 'abcd', partitionCount: 1, replicationFactor: 1 }]); + const connectedCuster = { clusterId: 'cluster1', clusterName: 'CLUSTER_1', clusterState: ClientState.connected }; + languageServiceConfig.setSelectedCluster(connectedCuster); + const languageService = getLanguageService(languageServiceConfig, languageServiceConfig, languageServiceConfig, languageServiceConfig); + + await assertHover( + 'PRODUCER\n' + + 'topic: ab|cd', + hover( + 'Topic `abcd`\n * partition count: `1`\n * replication factor: `1`\n', + position(1, 6), + position(1, 11) + ), + languageService + ); + + }); + + test("key property name Hover", async () => { + + await assertHover( + 'PRODUCER\n' + + 'ke|y: abcd', + hover( + 'The key *[optional]*.', + position(1, 0), + position(1, 3) + ) + ); + + }); + + test("key-format property name Hover", async () => { + + await assertHover( + 'PRODUCER\n' + + 'key-for|mat: string', + hover( + '[Serializer](https://github.com/jlandersen/vscode-kafka/blob/master/docs/Producing.md#Serializer) to use for the key *[optional]*.', + position(1, 0), + position(1, 10) + ) + ); + + }); + + test("key-format property value Hover", async () => { + + await assertHover( + 'PRODUCER\n' + + 'key-format: stri|ng', + hover( + 'Similar serializer to the Kafka Java client [org.apache.kafka.common.serialization.StringDeserializer](https://github.com/apache/kafka/blob/master/clients/src/main/java/org/apache/kafka/common/serialization/StringSerializer.java) which currently only supports `UTF-8` encoding.', + position(1, 12), + position(1, 18) + ) + ); + + }); + + test("value-format property name Hover", async () => { + + await assertHover( + 'PRODUCER\n' + + 'value-for|mat: string', + hover( + '[Serializer](https://github.com/jlandersen/vscode-kafka/blob/master/docs/Producing.md#Serializer) to use for the value *[optional]*.', + position(1, 0), + position(1, 12) + ) + ); + + }); + + test("value-format property value Hover", async () => { + + await assertHover( + 'PRODUCER\n' + + 'value-format: stri|ng', + hover( + 'Similar serializer to the Kafka Java client [org.apache.kafka.common.serialization.StringSerializer](https://github.com/apache/kafka/blob/master/clients/src/main/java/org/apache/kafka/common/serialization/Seserializer.java) which currently only supports `UTF-8` encoding.', + position(1, 14), + position(1, 20) + ) + ); + + }); + + }); + +}); \ No newline at end of file diff --git a/src/test/suite/kafka-file/languageservice/kafkaAssert.ts b/src/test/suite/kafka-file/languageservice/kafkaAssert.ts index 8d7f0c0d..976de9db 100644 --- a/src/test/suite/kafka-file/languageservice/kafkaAssert.ts +++ b/src/test/suite/kafka-file/languageservice/kafkaAssert.ts @@ -1,5 +1,5 @@ import * as assert from "assert"; -import { CodeLens, Position, Range, Command, Uri, workspace, CompletionList, SnippetString, Diagnostic, DiagnosticSeverity } from "vscode"; +import { CodeLens, Position, Range, Command, Uri, workspace, CompletionList, SnippetString, Diagnostic, DiagnosticSeverity, Hover, MarkdownString } from "vscode"; import { ClientState, ConsumerLaunchState } from "../../../../client"; import { BrokerConfigs } from "../../../../client/config"; import { ProducerLaunchState } from "../../../../client/producer"; @@ -12,7 +12,7 @@ export class LanguageServiceConfig implements ProducerLaunchStateProvider, Consu private consumerLaunchStates = new Map(); - private selectedCluster: { clusterId?: string, clusterName?: string, clusterState? : ClientState } | undefined; + private selectedCluster: { clusterId?: string, clusterName?: string, clusterState?: ClientState } | undefined; private topicsCache = new Map(); @@ -50,7 +50,7 @@ export class LanguageServiceConfig implements ProducerLaunchStateProvider, Consu return {}; } - public setSelectedCluster(selectedCluster: { clusterId?: string, clusterName?: string, clusterState? : ClientState }) { + public setSelectedCluster(selectedCluster: { clusterId?: string, clusterName?: string, clusterState?: ClientState }) { this.selectedCluster = selectedCluster; } @@ -66,11 +66,11 @@ export class LanguageServiceConfig implements ProducerLaunchStateProvider, Consu return topics.find(topic => topic.id === topicId); } - - public setAutoCreateConfig(autoCreateConfig : BrokerConfigs.AutoCreateTopicResult) { - this.autoCreateConfig= autoCreateConfig; + + public setAutoCreateConfig(autoCreateConfig: BrokerConfigs.AutoCreateTopicResult) { + this.autoCreateConfig = autoCreateConfig; } - + async getAutoCreateTopicEnabled(clusterid: string): Promise { return this.autoCreateConfig; } @@ -159,6 +159,29 @@ export async function assertDiagnostics(content: string, expected: Arrayactual.contents[0]).value, (expected.contents[0]).value); + } +} + // Kafka parser assert export interface ExpectedChunckResult {