Skip to content

Commit 9a5d38f

Browse files
committed
(GH-674) Puppetfile hover provider
Adds a Puppetfile hover provider that provides puppet module information when the user hovers over a module declaration line in a Puppetfile.
1 parent 12de07f commit 9a5d38f

File tree

3 files changed

+130
-0
lines changed

3 files changed

+130
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
99
### Added
1010

1111
- ([GH-666](https://github.com/puppetlabs/puppet-vscode/issues/666)) Add Puppetfile view to Puppet ToolBar
12+
- ([GH-674](https://github.com/puppetlabs/puppet-vscode/issues/674)) Add Puppetfile forge module hover provider
1213

1314
### Changed
1415

src/extension.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { BoltFeature } from './feature/BoltFeature';
1010
import { DebuggingFeature } from './feature/DebuggingFeature';
1111
import { FormatDocumentFeature } from './feature/FormatDocumentFeature';
1212
import { PDKFeature } from './feature/PDKFeature';
13+
import { PuppetfileHoverFeature } from './feature/PuppetfileHoverFeature';
1314
import { PuppetModuleHoverFeature } from './feature/PuppetModuleHoverFeature';
1415
import { PuppetNodeGraphFeature } from './feature/PuppetNodeGraphFeature';
1516
import { PuppetResourceFeature } from './feature/PuppetResourceFeature';
@@ -83,6 +84,7 @@ export function activate(context: vscode.ExtensionContext) {
8384
new BoltFeature(extContext),
8485
new UpdateConfigurationFeature(logger, extContext),
8586
statusBar,
87+
new PuppetfileHoverFeature(extContext, logger),
8688
];
8789

8890
if (configSettings.workspace.editorService.enable === false) {

src/feature/PuppetfileHoverFeature.ts

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// const axios = require('axios');
2+
import axios from 'axios';
3+
import * as vscode from 'vscode';
4+
import { IFeature } from '../feature';
5+
import { ILogger } from '../logging';
6+
7+
interface PuppetForgeModuleInfo {
8+
uri: string;
9+
slug: string;
10+
name: string;
11+
downloads: number;
12+
score: number;
13+
created: Date;
14+
updated: Date;
15+
endorsement: string;
16+
owner: { slug: string; username: string };
17+
forgeUrl: string;
18+
homepageUrl: string;
19+
version: number;
20+
summary: string;
21+
}
22+
23+
class PuppetfileHoverProvider implements vscode.HoverProvider {
24+
constructor(public readonly logger: ILogger) {}
25+
26+
async provideHover(
27+
document: vscode.TextDocument,
28+
position: vscode.Position,
29+
token: vscode.CancellationToken,
30+
): Promise<vscode.Hover> {
31+
if (token.isCancellationRequested) {
32+
return null;
33+
}
34+
35+
const range = document.getWordRangeAtPosition(position);
36+
const line = document.lineAt(position);
37+
if (line.isEmptyOrWhitespace) {
38+
return null;
39+
}
40+
41+
const text = line.text
42+
.replace(new RegExp('mod\\s+'), '')
43+
.replace(new RegExp(",\\s+'\\d.\\d.\\d\\'"), '')
44+
.replace(new RegExp(',\\s+:latest'), '')
45+
.replace("'", '')
46+
.replace("'", '');
47+
48+
const info = await this.getModuleInfo(text);
49+
const markdown = this.buildmarkdown(info);
50+
const hoverinfo = new vscode.Hover(markdown, range);
51+
return hoverinfo;
52+
}
53+
54+
private buildmarkdown(info: PuppetForgeModuleInfo): vscode.MarkdownString {
55+
const message = `## ${info.name}\n
56+
${info.summary}\n
57+
**Latest version:** ${info.version} (${info.created.toDateString()})\n
58+
**Forge**: [${info.forgeUrl}](${info.forgeUrl})\n
59+
**Project**: [${info.homepageUrl}](${info.homepageUrl})\n
60+
**Owner:** ${info.owner.username}\n
61+
**Endorsement:** ${info.endorsement?.toLocaleUpperCase()}\n
62+
**Score:** ${info.score}\n
63+
`;
64+
return new vscode.MarkdownString(message);
65+
}
66+
67+
private getModuleInfo(title: string): Promise<PuppetForgeModuleInfo> {
68+
return new Promise((resolve) => {
69+
return axios
70+
.get(`https://forgeapi.puppet.com/v3/modules/${title}`, {
71+
params: {
72+
// eslint-disable-next-line @typescript-eslint/camelcase
73+
exclude_fields: 'readme changelog license reference',
74+
},
75+
headers: {
76+
'Content-Type': 'application/json;charset=UTF-8',
77+
'User-Agent': 'puppet-vscode/0.27.0',
78+
},
79+
})
80+
.then((response) => {
81+
if (response.status !== 200) {
82+
this.logger.error(`Error getting Puppet forge data. Status: ${response.status}:${response.statusText}`);
83+
resolve();
84+
}
85+
86+
const info = response.data;
87+
const module = {
88+
uri: info.uri,
89+
slug: info.slug,
90+
name: info.name,
91+
downloads: info.downloads,
92+
score: info.feedback_score,
93+
created: new Date(info.created_at),
94+
updated: new Date(info.updated_at),
95+
endorsement: info.endorsement ?? '',
96+
forgeUrl: `https://forge.puppet.com/${info.owner.username}/${info.name}`,
97+
homepageUrl: info.homepage_url ?? '',
98+
version: info.current_release.version,
99+
owner: {
100+
uri: info.owner.uri,
101+
slug: info.owner.slug,
102+
username: info.owner.username,
103+
gravatar: info.owner.gravatar_id,
104+
},
105+
summary: info.current_release.metadata.summary,
106+
};
107+
108+
resolve(module);
109+
})
110+
.catch((error) => {
111+
this.logger.error(`Error getting Puppet forge data: ${error}`);
112+
resolve();
113+
});
114+
});
115+
}
116+
}
117+
118+
export class PuppetfileHoverFeature implements IFeature {
119+
constructor(public context: vscode.ExtensionContext, public logger: ILogger) {
120+
context.subscriptions.push(
121+
vscode.languages.registerHoverProvider('puppetfile', new PuppetfileHoverProvider(logger)),
122+
);
123+
}
124+
125+
// eslint-disable-next-line @typescript-eslint/no-empty-function
126+
dispose(): void {}
127+
}

0 commit comments

Comments
 (0)