From e5ca033591f171269f7cad5db3797bca888b0150 Mon Sep 17 00:00:00 2001 From: Benjamin von Polheim Date: Thu, 25 Nov 2021 21:22:31 +0100 Subject: [PATCH] feat: respect latest and retired versions --- src/hexpm.ts | 47 +++++++++++++++++++++++++++-- src/provide.ts | 81 ++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 107 insertions(+), 21 deletions(-) diff --git a/src/hexpm.ts b/src/hexpm.ts index 3543c3c..8d9ea47 100644 --- a/src/hexpm.ts +++ b/src/hexpm.ts @@ -2,7 +2,50 @@ import axios from 'axios'; const BASE_URL = 'https://hex.pm/api'; -export function getPackage(name: String) { +export function getPackage(name: String): Promise { const url = `${BASE_URL}/packages/${name}`; - return axios.get(url).then(res => res.data); + return axios.get(url).then((res) => res.data); } + +export type HexPackage = { + configs: Record; + docs_html_url: string; + downloads: { + all: number; + day: number; + recent: number; + week: number; + }; + html_url: string; + inserted_at: string; + latest_stable_version: string; + latest_version: string; + meta: Record; + name: string; + owners: HexAuthor[]; + releases: HexRelease[]; + repository: string; + retirements: HexRetirements; + updated_at: string; + url: string; +}; + +export type HexAuthor = { + email: string; + url: string; + username: string; +}; + +export type HexRelease = { + has_docs: boolean; + inserted_at: string; + url: string; + version: string; +}; + +export type HexRetirement = { + message: string; + reason: string; +}; + +export type HexRetirements = Record; diff --git a/src/provide.ts b/src/provide.ts index e96fe55..3c6c442 100644 --- a/src/provide.ts +++ b/src/provide.ts @@ -3,7 +3,10 @@ import * as hexpm from './hexpm'; const semver = require('semver'); -export function provide(document: vscode.TextDocument, position: vscode.Position): Thenable { +export function provide( + document: vscode.TextDocument, + position: vscode.Position, +): Thenable { const line = document.lineAt(position.line); const packageName = getPackageName(line, position); @@ -15,10 +18,11 @@ export function provide(document: vscode.TextDocument, position: vscode.Position } function getVersionsForPackage(packageName: String): Promise { - return hexpm.getPackage(packageName) - .then(res => completionItemsForReleases(res.releases)) - .then(sortCompletionItems) - .catch(error => { + return hexpm + .getPackage(packageName) + .then(partitionReleases) + .then(createCompletionItems) + .catch((error) => { if (error.response.status === 404) { return []; } @@ -46,23 +50,62 @@ function tupleBeginIndex(line: vscode.TextLine, position: vscode.Position): numb return tupleBeginIndex; } -function sortCompletionItems(completionItems: vscode.CompletionItem[]): vscode.CompletionItem[] { - // descending sort using semver: most recent version first - const sorted = completionItems.sort((a, b) => - semver.rcompare(a.label, b.label) - ); - // comply with js lexicographic sorting as vscode does not allow alternative sorting - // maintain sort using 0-9 prefixed with z for each place value - sorted.forEach((item, idx) => - item.sortText = `${'z'.repeat(Math.trunc(idx/10))}${idx%10}` +type PartitionedRelease = { + stable?: hexpm.HexRelease; + rest: hexpm.HexRelease[]; + retired: hexpm.HexRelease[]; +}; + +function partitionReleases(p: hexpm.HexPackage): PartitionedRelease { + // filter out retired packages + const retired = p.releases.filter((r) => Object.keys(p.retirements).includes(r.version)); + const stable = p.releases.find((r) => r.version === p.latest_stable_version); + const rest = p.releases.filter( + (r) => retired.every((x) => x.version !== r.version) && r.version !== stable?.version, ); - return sorted; + return { + stable, + retired, + rest, + }; } -function completionItemsForReleases(releases: any[]): vscode.CompletionItem[] { - return releases.map((rel, index, arr) => { - const completionItem = new vscode.CompletionItem(rel.version, vscode.CompletionItemKind.Property); - return completionItem; +function createCompletionItems(releases: PartitionedRelease): vscode.CompletionItem[] { + const stable = releases.stable + ? new vscode.CompletionItem(releases.stable.version, vscode.CompletionItemKind.Property) + : null; + + if (stable) { + stable.detail = 'latest stable'; + } + + const retired = releases.retired.map((r) => { + const item = new vscode.CompletionItem(r.version, vscode.CompletionItemKind.Property); + item.detail = 'retired'; + return item; + }); + + const rest = releases.rest.map((r) => { + return new vscode.CompletionItem(r.version, vscode.CompletionItemKind.Property); }); + + const sortBySemver = (a: vscode.CompletionItem, b: vscode.CompletionItem) => + semver.compare(b.label, a.label); + + const sorted = [ + ...(stable ? [stable] : []), + ...[...rest].sort(sortBySemver), + ...[...retired].sort(sortBySemver), + ]; + + sorted.forEach(applySortText); + + return sorted; +} + +function applySortText(item: vscode.CompletionItem, index: number) { + item.sortText = `${'z'.repeat(Math.trunc(index / 10))}${index % 10}`; + + return item; }