Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
13 changes: 12 additions & 1 deletion packages/core/src/dev/activation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { DevNotificationsState } from '../notifications/types'
import { QuickPickItem } from 'vscode'
import { ChildProcess } from '../shared/utilities/processUtils'
import { WorkspaceLSPResolver } from '../amazonq/lsp/workspaceInstaller'
import * as ManifestResolver from '../shared/lsp/manifestResolver'

interface MenuOption {
readonly label: string
Expand Down Expand Up @@ -453,10 +454,16 @@ const resettableFeatures: readonly ResettableFeature[] = [
},
{
name: 'workspace lsp',
label: 'Lsp',
label: 'Lsp Download',
detail: 'Resets workspace LSP',
executor: resetWorkspaceLspDownload,
},
{
name: 'lsp global state',
label: 'Lsp State',
detail: 'Resets LSP manifest global state',
executor: resetLSPGlobalState,
},
] as const

// TODO this is *somewhat* similar to `openStorageFromInput`. If we need another
Expand Down Expand Up @@ -549,6 +556,10 @@ async function resetWorkspaceLspDownload() {
await new WorkspaceLSPResolver().resolve()
}

async function resetLSPGlobalState() {
await ManifestResolver.resetManifestState()
}

async function editNotifications() {
const storageKey = 'aws.notifications.dev'
const current = globalState.get(storageKey) ?? {}
Expand Down
43 changes: 37 additions & 6 deletions packages/core/src/shared/lsp/manifestResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,33 @@
* SPDX-License-Identifier: Apache-2.0
*/

import * as vscode from 'vscode'
import { getLogger } from '../logger/logger'
import { ToolkitError } from '../errors'
import { Timeout } from '../utilities/timeoutUtils'
import globals from '../extensionGlobals'
import { Manifest } from './types'
import { StageResolver, tryStageResolvers } from './utils/setupStage'
import { HttpResourceFetcher } from '../resourcefetcher/httpResourceFetcher'
import * as localizedText from '../localizedText'

const logger = getLogger('lsp')

interface StorageManifest {
etag: string
content: string
muteDeprecation: boolean
}

type ManifestStorage = Record<string, StorageManifest>

export const manifestStorageKey = 'aws.toolkit.lsp.manifest'
const manifestTimeoutMs = 15000

export async function resetManifestState() {
await globals.globalState.update(manifestStorageKey, {})
}

export class ManifestResolver {
constructor(
private readonly manifestURL: string,
Expand Down Expand Up @@ -60,7 +67,9 @@ export class ManifestResolver {
}).getNewETagContent(this.getEtag())

if (!resp.content) {
throw new ToolkitError('New content was not downloaded; fallback to the locally stored manifest')
throw new ToolkitError(
`New content was not downloaded; fallback to the locally stored ${this.lsName} manifest`
)
}

const manifest = this.parseManifest(resp.content)
Expand All @@ -71,12 +80,12 @@ export class ManifestResolver {
}

private async getLocalManifest(): Promise<Manifest> {
logger.info('Failed to download latest LSP manifest. Falling back to local manifest.')
logger.info(`Failed to download latest ${this.lsName} manifest. Falling back to local manifest.`)
const storage = this.getStorage()
const manifestData = storage[this.lsName]

if (!manifestData?.content) {
throw new ToolkitError('Failed to download LSP manifest and no local manifest found.')
throw new ToolkitError(`Failed to download ${this.lsName} manifest and no local manifest found.`)
}

const manifest = this.parseManifest(manifestData.content)
Expand All @@ -90,25 +99,47 @@ export class ManifestResolver {
return JSON.parse(content) as Manifest
} catch (error) {
throw new ToolkitError(
`Failed to parse manifest: ${error instanceof Error ? error.message : 'Unknown error'}`
`Failed to parse ${this.lsName} manifest: ${error instanceof Error ? error.message : 'Unknown error'}`
)
}
}

/**
* Check if the current manifest is deprecated.
* If yes and user hasn't muted this notification, shows a toast message with two buttons:
* - OK: close and do nothing
* - Don't Show Again: Update global state (muteDecprecation) so the deprecation message is never shown for this manifest.
* @param manifest
*/
private checkDeprecation(manifest: Manifest): void {
if (manifest.isManifestDeprecated) {
logger.info('This LSP manifest is deprecated. No future updates will be available.')
if (manifest.isManifestDeprecated && !this.getStorage()[this.lsName].muteDeprecation) {
const deprecationMessage = `${this.lsName} manifest is deprecated. No future updates will be available.`
logger.info(deprecationMessage)

void vscode.window
.showInformationMessage(deprecationMessage, localizedText.ok, localizedText.dontShow)
.then((button) => {
if (button === localizedText.dontShow) {
this.getStorage()[this.lsName].muteDeprecation = true
}
})
}
}

private async saveManifest(etag: string, content: string): Promise<void> {
const storage = this.getStorage()

// Only true when incoming manifest is deprecated & existing muteDeprecation is true (set by user)
const muteDeprecation =
(storage[this.lsName] ? storage[this.lsName].muteDeprecation : false) &&
(JSON.parse(content) as Manifest).isManifestDeprecated

globals.globalState.tryUpdate(manifestStorageKey, {
...storage,
[this.lsName]: {
etag,
content,
muteDeprecation,
},
})
}
Expand Down
Loading