diff --git a/lsp/.vscode/launch.json b/lsp/.vscode/launch.json index de6d0215..0b46dc7b 100644 --- a/lsp/.vscode/launch.json +++ b/lsp/.vscode/launch.json @@ -37,6 +37,19 @@ }, "preLaunchTask": "npm: compile" }, + { + "name": "CodeWhisperer Server", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": ["--extensionDevelopmentPath=${workspaceFolder}/client/vscode"], + "outFiles": ["${workspaceFolder}/client/vscode/out/**/*.js"], + "env": { + "LSP_SERVER": "${workspaceFolder}/app/aws-lsp-codewhisperer-binary/out/index.js", + "ENABLE_INLINE_COMPLETION": "true" + }, + "preLaunchTask": "npm: compile" + }, { "name": "S3 Server", "type": "extensionHost", diff --git a/lsp/app/aws-lsp-codewhisperer-binary/package.json b/lsp/app/aws-lsp-codewhisperer-binary/package.json new file mode 100644 index 00000000..ecdda982 --- /dev/null +++ b/lsp/app/aws-lsp-codewhisperer-binary/package.json @@ -0,0 +1,19 @@ +{ + "name": "@lsp-placeholder/aws-lsp-codewhisperer-binary", + "version": "0.0.1", + "description": "CodeWhisperer Language Server Binary", + "main": "out/index.js", + "bin": { + "aws-lsp-codewhisperer-binary": "./out/index.js" + }, + "scripts": { + "compile": "tsc --build", + "package-x64": "pkg --targets node18-linux-x64,node18-win-x64,node18-macos-x64 --out-path bin --compress GZip ." + }, + "dependencies": { + "@lsp-placeholder/aws-lsp-codewhisperer": "^0.0.1" + }, + "devDependencies": { + "pkg": "^5.8.1" + } +} diff --git a/lsp/app/aws-lsp-codewhisperer-binary/src/index.ts b/lsp/app/aws-lsp-codewhisperer-binary/src/index.ts new file mode 100644 index 00000000..44504f33 --- /dev/null +++ b/lsp/app/aws-lsp-codewhisperer-binary/src/index.ts @@ -0,0 +1,23 @@ +import { + CodeWhispererServer, + CodeWhispererServerProps, + CodeWhispererServiceProps, + createCodeWhispererService, +} from '@lsp-placeholder/aws-lsp-codewhisperer' +import { ProposedFeatures, createConnection } from 'vscode-languageserver/node' + +const connection = createConnection(ProposedFeatures.all) + +const serviceProps: CodeWhispererServiceProps = { + displayName: CodeWhispererServer.serverId, + connection, +} + +const service = createCodeWhispererService(serviceProps) + +const props: CodeWhispererServerProps = { + connection, + codeWhispererService: service, +} + +export const server = new CodeWhispererServer(props) diff --git a/lsp/app/aws-lsp-codewhisperer-binary/tsconfig.json b/lsp/app/aws-lsp-codewhisperer-binary/tsconfig.json new file mode 100644 index 00000000..0b9cfe9b --- /dev/null +++ b/lsp/app/aws-lsp-codewhisperer-binary/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.packages.json", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./out" + }, + "include": ["src"] +} diff --git a/lsp/client/vscode/package.json b/lsp/client/vscode/package.json index f63b414e..4dc82cd3 100644 --- a/lsp/client/vscode/package.json +++ b/lsp/client/vscode/package.json @@ -16,7 +16,8 @@ }, "main": "./out/extension.js", "activationEvents": [ - "onLanguage:yaml" + "onLanguage:yaml", + "onLanguage:typescript" ], "contributes": { "configuration": { diff --git a/lsp/client/vscode/src/activation.ts b/lsp/client/vscode/src/activation.ts index b12856ae..31639b14 100644 --- a/lsp/client/vscode/src/activation.ts +++ b/lsp/client/vscode/src/activation.ts @@ -8,6 +8,7 @@ import * as path from 'path' import { ExtensionContext, workspace } from 'vscode' import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient/node' +import { registerInlineCompletion } from './inlineCompletionActivation' export async function activateDocumentsLanguageServer(extensionContext: ExtensionContext) { /** @@ -28,6 +29,14 @@ export async function activateDocumentsLanguageServer(extensionContext: Extensio const fallbackPath = path.join(extensionContext.extensionPath, '../../../out/src/server/server.js') const serverModule = process.env.LSP_SERVER ?? fallbackPath + /** + * If you are iterating with a language server that uses inline completion, + * set the ENABLE_INLINE_COMPLETION environment variable to "true". + * This will set up the extension's inline completion provider to get recommendations + * from the language server. + */ + const enableInlineCompletion = process.env.ENABLE_INLINE_COMPLETION === 'true' + const debugOptions = { execArgv: ['--nolazy', '--inspect=6012', '--preserve-symlinks'] } // If the extension is launch in debug mode the debug server options are use @@ -41,19 +50,27 @@ export async function activateDocumentsLanguageServer(extensionContext: Extensio const clientOptions: LanguageClientOptions = { // Register the server for json documents documentSelector: [ + // yaml/json is illustrative of static filetype handling language servers { scheme: 'file', language: 'yaml' }, { scheme: 'untitled', language: 'yaml' }, { scheme: 'file', language: 'json' }, { scheme: 'untitled', language: 'json' }, + // typescript is illustrative of code-handling language servers + { scheme: 'file', language: 'typescript' }, + { scheme: 'untitled', language: 'typescript' }, ], synchronize: { - fileEvents: workspace.createFileSystemWatcher('**/*.{json,yml,yaml}'), + fileEvents: workspace.createFileSystemWatcher('**/*.{json,yml,yaml,ts}'), }, } // Create the language client and start the client. const client = new LanguageClient('awsDocuments', 'AWS Documents Language Server', serverOptions, clientOptions) + if (enableInlineCompletion) { + registerInlineCompletion(client) + } + client.start() return client diff --git a/lsp/client/vscode/src/futureTypes.ts b/lsp/client/vscode/src/futureTypes.ts new file mode 100644 index 00000000..a3cc7a23 --- /dev/null +++ b/lsp/client/vscode/src/futureTypes.ts @@ -0,0 +1,45 @@ +import { InlineCompletionContext, InlineCompletionItem, InlineCompletionList } from 'vscode' +import { + ProtocolRequestType, + StaticRegistrationOptions, + TextDocumentPositionParams, + TextDocumentRegistrationOptions, + WorkDoneProgressOptions, + WorkDoneProgressParams, +} from 'vscode-languageclient' + +/** + * Inline completion is not a part of the language server protocol. + * It is being proposed at this time (https://github.com/microsoft/language-server-protocol/pull/1673). + * + * This file contains boilerplate code that goes away if that proposal goes mainline. + * The proposal is being modelled after the VS Code extensibility APIs, so the types + * used from `vscode` are compatible with what is being planned for future + * `vscode-languageclient` types. + * + * See remarks in server\aws-lsp-codewhisperer\src\language-server\codeWhispererServer.ts + * for more details. + */ + +type InlineCompletionOptions = WorkDoneProgressOptions + +type InlineCompletionRegistrationOptions = InlineCompletionOptions & + TextDocumentRegistrationOptions & + StaticRegistrationOptions + +export type InlineCompletionParams = WorkDoneProgressParams & + TextDocumentPositionParams & { + context: InlineCompletionContext + } + +/** + * inlineCompletionRequestType defines the custom method that the language client + * requests from the server to provide inline completion recommendations. + */ +export const inlineCompletionRequestType = new ProtocolRequestType< + InlineCompletionParams, + InlineCompletionList | InlineCompletionItem[] | null, + InlineCompletionItem[], + void, + InlineCompletionRegistrationOptions +>('aws/textDocument/inlineCompletion') diff --git a/lsp/client/vscode/src/inlineCompletionActivation.ts b/lsp/client/vscode/src/inlineCompletionActivation.ts new file mode 100644 index 00000000..0869c8a1 --- /dev/null +++ b/lsp/client/vscode/src/inlineCompletionActivation.ts @@ -0,0 +1,43 @@ +import { + CancellationToken, + InlineCompletionContext, + InlineCompletionItem, + InlineCompletionItemProvider, + InlineCompletionList, + Position, + TextDocument, + languages, +} from 'vscode' +import { LanguageClient } from 'vscode-languageclient/node' +import { InlineCompletionParams, inlineCompletionRequestType } from './futureTypes' + +export function registerInlineCompletion(languageClient: LanguageClient) { + const inlineCompletionProvider = new CodeWhispererInlineCompletionItemProvider(languageClient) + languages.registerInlineCompletionItemProvider({ scheme: 'file', language: 'typescript' }, inlineCompletionProvider) +} + +class CodeWhispererInlineCompletionItemProvider implements InlineCompletionItemProvider { + constructor(private readonly languageClient: LanguageClient) {} + + async provideInlineCompletionItems( + document: TextDocument, + position: Position, + context: InlineCompletionContext, + token: CancellationToken + ): Promise { + const request: InlineCompletionParams = { + textDocument: { + uri: document.uri.toString(), + }, + position, + context, + } + + const response = await this.languageClient.sendRequest(inlineCompletionRequestType, request, token) + + const list: InlineCompletionList = response as InlineCompletionList + this.languageClient.info(`Client: Received ${list.items.length} suggestions`) + + return list + } +} diff --git a/lsp/package-lock.json b/lsp/package-lock.json index d1e3ca28..45f3da9e 100644 --- a/lsp/package-lock.json +++ b/lsp/package-lock.json @@ -54,6 +54,19 @@ "pkg": "^5.8.1" } }, + "app/aws-lsp-codewhisperer-binary": { + "name": "@lsp-placeholder/aws-lsp-codewhisperer-binary", + "version": "0.0.1", + "dependencies": { + "@lsp-placeholder/aws-lsp-codewhisperer": "^0.0.1" + }, + "bin": { + "aws-lsp-codewhisperer-binary": "out/index.js" + }, + "devDependencies": { + "pkg": "^5.8.1" + } + }, "app/aws-lsp-s3-binary": { "name": "@lsp-placeholder/aws-lsp-s3-binary", "version": "0.0.1", @@ -1972,6 +1985,14 @@ "resolved": "app/aws-lsp-cloudformation-binary", "link": true }, + "node_modules/@lsp-placeholder/aws-lsp-codewhisperer": { + "resolved": "server/aws-lsp-codewhisperer", + "link": true + }, + "node_modules/@lsp-placeholder/aws-lsp-codewhisperer-binary": { + "resolved": "app/aws-lsp-codewhisperer-binary", + "link": true + }, "node_modules/@lsp-placeholder/aws-lsp-core": { "resolved": "core/aws-lsp-core", "link": true @@ -2589,6 +2610,60 @@ "node": ">= 4.0.0" } }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-sdk": { + "version": "2.1403.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1403.0.tgz", + "integrity": "sha512-qRhiE2iqfXhTEsGJ0unNY7mz5Y7YA4ljEh/1lU/HaUI8hPCQtmZjU3P2/w2YcHFaSzJsu9J9svzjkALfHpetoA==", + "dependencies": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aws-sdk/node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/aws-sdk/node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "node_modules/aws-sdk/node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/awsdocuments-ls-client": { "resolved": "client/vscode", "link": true @@ -2603,7 +2678,6 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, "funding": [ { "type": "github", @@ -2710,6 +2784,18 @@ "ieee754": "^1.1.13" } }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2863,6 +2949,25 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/copyfiles": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.4.1.tgz", + "integrity": "sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==", + "dev": true, + "dependencies": { + "glob": "^7.0.5", + "minimatch": "^3.0.3", + "mkdirp": "^1.0.4", + "noms": "0.0.0", + "through2": "^2.0.1", + "untildify": "^4.0.0", + "yargs": "^16.1.0" + }, + "bin": { + "copyfiles": "copyfiles", + "copyup": "copyfiles" + } + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -3288,6 +3393,14 @@ "node": ">=0.10.0" } }, + "node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/execa": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", @@ -3460,6 +3573,14 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, "node_modules/from2": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", @@ -3514,8 +3635,7 @@ "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "node_modules/get-caller-file": { "version": "2.0.5", @@ -3535,6 +3655,20 @@ "node": "*" } }, + "node_modules/get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", @@ -3623,6 +3757,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -3645,7 +3790,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "dependencies": { "function-bind": "^1.1.1" }, @@ -3662,6 +3806,42 @@ "node": ">=8" } }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -3775,8 +3955,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { "version": "1.3.8", @@ -3800,6 +3979,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -3812,6 +4006,17 @@ "node": ">=8" } }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-core-module": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", @@ -3842,6 +4047,20 @@ "node": ">=8" } }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -3893,6 +4112,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -3908,8 +4145,7 @@ "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, "node_modules/isexe": { "version": "2.0.0", @@ -3917,6 +4153,14 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -4147,6 +4391,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", @@ -4508,6 +4764,40 @@ } } }, + "node_modules/noms": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz", + "integrity": "sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "~1.0.31" + } + }, + "node_modules/noms/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "node_modules/noms/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/noms/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -4895,6 +5185,15 @@ "node": ">=6" } }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5089,6 +5388,11 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, + "node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" + }, "node_modules/semver": { "version": "7.5.1", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", @@ -5363,6 +5667,16 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -5597,6 +5911,15 @@ "node": ">= 10.0.0" } }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -5605,6 +5928,32 @@ "punycode": "^2.1.0" } }, + "node_modules/url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -5775,6 +6124,25 @@ "node": ">= 8" } }, + "node_modules/which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -5813,6 +6181,35 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -6026,6 +6423,19 @@ "vscode-languageserver-textdocument": "^1.0.8" } }, + "server/aws-lsp-codewhisperer": { + "name": "@lsp-placeholder/aws-lsp-codewhisperer", + "version": "0.0.1", + "dependencies": { + "@lsp-placeholder/aws-lsp-core": "^0.0.1", + "aws-sdk": "^2.1403.0", + "vscode-languageserver": "^8.0.1", + "vscode-languageserver-textdocument": "^1.0.8" + }, + "devDependencies": { + "copyfiles": "^2.4.1" + } + }, "server/aws-lsp-s3": { "name": "@lsp-placeholder/aws-lsp-s3", "version": "0.0.1", diff --git a/lsp/package.json b/lsp/package.json index dba21241..b713bb7d 100644 --- a/lsp/package.json +++ b/lsp/package.json @@ -15,7 +15,7 @@ "format": "prettier --write .", "format-staged": "npx pretty-quick --staged", "clean": "ts-node ./script/clean.ts", - "compile": "tsc --build --verbose", + "compile": "tsc --build --verbose && npm run postcompile --workspaces --if-present", "watch": "tsc --build --watch", "test": "npm run test --workspaces --if-present", "package": "npm install && npm run compile && npm run package-x64 --workspaces --if-present" diff --git a/lsp/server/aws-lsp-codewhisperer/package.json b/lsp/server/aws-lsp-codewhisperer/package.json new file mode 100644 index 00000000..8499cc2e --- /dev/null +++ b/lsp/server/aws-lsp-codewhisperer/package.json @@ -0,0 +1,20 @@ +{ + "name": "@lsp-placeholder/aws-lsp-codewhisperer", + "version": "0.0.1", + "description": "CodeWhisperer Language Server", + "main": "out/index.js", + "scripts": { + "compile": "tsc --build", + "postcompile": "npm run copyServiceClient", + "copyServiceClient": "copyfiles -u 1 --error ./src/client/*.json out" + }, + "dependencies": { + "@lsp-placeholder/aws-lsp-core": "^0.0.1", + "aws-sdk": "^2.1403.0", + "vscode-languageserver": "^8.0.1", + "vscode-languageserver-textdocument": "^1.0.8" + }, + "devDependencies": { + "copyfiles": "^2.4.1" + } +} diff --git a/lsp/server/aws-lsp-codewhisperer/src/client/codewhisperer.ts b/lsp/server/aws-lsp-codewhisperer/src/client/codewhisperer.ts new file mode 100644 index 00000000..6717e9b8 --- /dev/null +++ b/lsp/server/aws-lsp-codewhisperer/src/client/codewhisperer.ts @@ -0,0 +1,13 @@ +import { Service } from 'aws-sdk' +import { ServiceConfigurationOptions } from 'aws-sdk/lib/service' +const apiConfig = require('./service-2.json') +import CodeWhispererClient = require('./codewhispererclient') + +export function createCodeWhispererClient(options: ServiceConfigurationOptions): CodeWhispererClient { + return createService(options) as CodeWhispererClient +} + +function createService(options: ServiceConfigurationOptions): Service { + const client = new Service({ apiConfig, ...options } as any) + return client +} diff --git a/lsp/server/aws-lsp-codewhisperer/src/client/codewhispererclient.d.ts b/lsp/server/aws-lsp-codewhisperer/src/client/codewhispererclient.d.ts new file mode 100644 index 00000000..fd2de04b --- /dev/null +++ b/lsp/server/aws-lsp-codewhisperer/src/client/codewhispererclient.d.ts @@ -0,0 +1,106 @@ +/** + * THIS FILE IS AUTOGENERATED BY 'generateServiceClient.ts'. + * DO NOT EDIT BY HAND. + */ + +import { ConfigBase as Config } from 'aws-sdk/lib/config-base' +import { AWSError } from 'aws-sdk/lib/error' +import { Request } from 'aws-sdk/lib/request' +import { Service, ServiceConfigurationOptions } from 'aws-sdk/lib/service' +interface Blob {} +declare class CodeWhispererClient extends Service { + /** + * Constructs a service object. This object has one method for each API operation. + */ + constructor(options?: CodeWhispererClient.Types.ClientConfiguration) + config: Config & CodeWhispererClient.Types.ClientConfiguration + /** + * + */ + generateRecommendations( + params: CodeWhispererClient.Types.GenerateRecommendationsRequest, + callback?: (err: AWSError, data: CodeWhispererClient.Types.GenerateRecommendationsResponse) => void + ): Request + /** + * + */ + generateRecommendations( + callback?: (err: AWSError, data: CodeWhispererClient.Types.GenerateRecommendationsResponse) => void + ): Request +} +declare namespace CodeWhispererClient { + export interface FileContext { + leftFileContent: FileContextLeftFileContentString + rightFileContent: FileContextRightFileContentString + filename: FileContextFilenameString + programmingLanguage: ProgrammingLanguage + } + export type FileContextFilenameString = string + export type FileContextLeftFileContentString = string + export type FileContextRightFileContentString = string + export interface GenerateRecommendationsRequest { + fileContext: FileContext + maxResults?: GenerateRecommendationsRequestMaxResultsInteger + nextToken?: GenerateRecommendationsRequestNextTokenString + referenceTrackerConfiguration?: ReferenceTrackerConfiguration + } + export type GenerateRecommendationsRequestMaxResultsInteger = number + export type GenerateRecommendationsRequestNextTokenString = string + export interface GenerateRecommendationsResponse { + recommendations?: RecommendationsList + nextToken?: String + } + export interface Import { + statement?: ImportStatementString + } + export type ImportStatementString = string + export type Imports = Import[] + export interface ProgrammingLanguage { + languageName: ProgrammingLanguageLanguageNameString + } + export type ProgrammingLanguageLanguageNameString = string + export interface Recommendation { + content: RecommendationContentString + references?: References + mostRelevantMissingImports?: Imports + } + export type RecommendationContentString = string + export type RecommendationsList = Recommendation[] + export type RecommendationsWithReferencesPreference = 'BLOCK' | 'ALLOW' | string + export interface Reference { + licenseName?: ReferenceLicenseNameString + repository?: ReferenceRepositoryString + url?: ReferenceUrlString + recommendationContentSpan?: Span + } + export type ReferenceLicenseNameString = string + export type ReferenceRepositoryString = string + export interface ReferenceTrackerConfiguration { + recommendationsWithReferences: RecommendationsWithReferencesPreference + } + export type ReferenceUrlString = string + export type References = Reference[] + export interface Span { + start?: SpanStartInteger + end?: SpanEndInteger + } + export type SpanEndInteger = number + export type SpanStartInteger = number + export type String = string + /** + * A string in YYYY-MM-DD format that represents the latest possible API version that can be used in this service. Specify 'latest' to use the latest possible version. + */ + export type apiVersion = '2022-06-15' | 'latest' | string + export interface ClientApiVersions { + /** + * A string in YYYY-MM-DD format that represents the latest possible API version that can be used in this service. Specify 'latest' to use the latest possible version. + */ + apiVersion?: apiVersion + } + export type ClientConfiguration = ServiceConfigurationOptions & ClientApiVersions + /** + * Contains interfaces for use with the CodeWhispererClient client. + */ + export import Types = CodeWhispererClient +} +export = CodeWhispererClient diff --git a/lsp/server/aws-lsp-codewhisperer/src/client/service-2.json b/lsp/server/aws-lsp-codewhisperer/src/client/service-2.json new file mode 100644 index 00000000..7afebbb7 --- /dev/null +++ b/lsp/server/aws-lsp-codewhisperer/src/client/service-2.json @@ -0,0 +1,346 @@ +{ + "version": "2.0", + "metadata": { + "apiVersion": "2022-06-15", + "endpointPrefix": "codewhisperer", + "jsonVersion": "1.0", + "protocol": "json", + "serviceFullName": "AWS CodeWhisperer", + "serviceId": "CodeWhisperer", + "signatureVersion": "v4", + "signingName": "codewhisperer", + "targetPrefix": "AWSCodeWhispererService", + "uid": "codewhisperer-2022-06-15" + }, + "operations": { + "GenerateRecommendations": { + "name": "GenerateRecommendations", + "http": { + "method": "POST", + "requestUri": "/" + }, + "input": { + "shape": "GenerateRecommendationsRequest" + }, + "output": { + "shape": "GenerateRecommendationsResponse" + }, + "errors": [ + { + "shape": "ThrottlingException" + }, + { + "shape": "InternalServerException" + }, + { + "shape": "ValidationException" + }, + { + "shape": "AccessDeniedException" + } + ] + } + }, + "shapes": { + "AccessDeniedException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { + "shape": "String" + } + }, + "exception": true + }, + "ConflictException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { + "shape": "String" + } + }, + "exception": true + }, + "FileContext": { + "type": "structure", + "required": ["leftFileContent", "rightFileContent", "filename", "programmingLanguage"], + "members": { + "leftFileContent": { + "shape": "FileContextLeftFileContentString" + }, + "rightFileContent": { + "shape": "FileContextRightFileContentString" + }, + "filename": { + "shape": "FileContextFilenameString" + }, + "programmingLanguage": { + "shape": "ProgrammingLanguage" + } + } + }, + "FileContextFilenameString": { + "type": "string", + "max": 1024, + "min": 1 + }, + "FileContextLeftFileContentString": { + "type": "string", + "max": 10240, + "min": 0, + "sensitive": true + }, + "FileContextRightFileContentString": { + "type": "string", + "max": 10240, + "min": 0, + "sensitive": true + }, + "GenerateRecommendationsRequest": { + "type": "structure", + "required": ["fileContext"], + "members": { + "fileContext": { + "shape": "FileContext" + }, + "maxResults": { + "shape": "GenerateRecommendationsRequestMaxResultsInteger" + }, + "nextToken": { + "shape": "GenerateRecommendationsRequestNextTokenString" + }, + "referenceTrackerConfiguration": { + "shape": "ReferenceTrackerConfiguration" + } + } + }, + "GenerateRecommendationsRequestMaxResultsInteger": { + "type": "integer", + "box": true, + "max": 10, + "min": 1 + }, + "GenerateRecommendationsRequestNextTokenString": { + "type": "string", + "max": 2048, + "min": 0, + "pattern": "(?:[A-Za-z0-9\\+/]{4})*(?:[A-Za-z0-9\\+/]{2}\\=\\=|[A-Za-z0-9\\+/]{3}\\=)?" + }, + "GenerateRecommendationsResponse": { + "type": "structure", + "members": { + "recommendations": { + "shape": "RecommendationsList" + }, + "nextToken": { + "shape": "String" + } + } + }, + "IdempotencyToken": { + "type": "string", + "max": 256, + "min": 1 + }, + "Import": { + "type": "structure", + "members": { + "statement": { + "shape": "ImportStatementString" + } + } + }, + "ImportStatementString": { + "type": "string", + "max": 1024, + "min": 1, + "sensitive": true + }, + "Imports": { + "type": "list", + "member": { + "shape": "Import" + }, + "max": 10, + "min": 0 + }, + "InternalServerException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { + "shape": "String" + } + }, + "exception": true, + "fault": true, + "retryable": { + "throttling": false + } + }, + "ProgrammingLanguage": { + "type": "structure", + "required": ["languageName"], + "members": { + "languageName": { + "shape": "ProgrammingLanguageLanguageNameString" + } + } + }, + "ProgrammingLanguageLanguageNameString": { + "type": "string", + "max": 128, + "min": 1, + "pattern": "(python|javascript|java|csharp|typescript|c|cpp|go|kotlin|php|ruby|rust|scala|shell|sql)" + }, + "Recommendation": { + "type": "structure", + "required": ["content"], + "members": { + "content": { + "shape": "RecommendationContentString" + }, + "references": { + "shape": "References" + }, + "mostRelevantMissingImports": { + "shape": "Imports" + } + } + }, + "RecommendationContentString": { + "type": "string", + "max": 5120, + "min": 1, + "sensitive": true + }, + "RecommendationsList": { + "type": "list", + "member": { + "shape": "Recommendation" + }, + "max": 10, + "min": 0 + }, + "RecommendationsWithReferencesPreference": { + "type": "string", + "enum": ["BLOCK", "ALLOW"] + }, + "Reference": { + "type": "structure", + "members": { + "licenseName": { + "shape": "ReferenceLicenseNameString" + }, + "repository": { + "shape": "ReferenceRepositoryString" + }, + "url": { + "shape": "ReferenceUrlString" + }, + "recommendationContentSpan": { + "shape": "Span" + } + } + }, + "ReferenceLicenseNameString": { + "type": "string", + "max": 1024, + "min": 1 + }, + "ReferenceRepositoryString": { + "type": "string", + "max": 1024, + "min": 1 + }, + "ReferenceTrackerConfiguration": { + "type": "structure", + "required": ["recommendationsWithReferences"], + "members": { + "recommendationsWithReferences": { + "shape": "RecommendationsWithReferencesPreference" + } + } + }, + "ReferenceUrlString": { + "type": "string", + "max": 1024, + "min": 1 + }, + "References": { + "type": "list", + "member": { + "shape": "Reference" + }, + "max": 10, + "min": 0 + }, + "ResourceArn": { + "type": "string", + "max": 1224, + "min": 0 + }, + "ResourceNotFoundException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { + "shape": "String" + } + }, + "exception": true + }, + "SensitiveString": { + "type": "string", + "sensitive": true + }, + "Span": { + "type": "structure", + "members": { + "start": { + "shape": "SpanStartInteger" + }, + "end": { + "shape": "SpanEndInteger" + } + } + }, + "SpanEndInteger": { + "type": "integer", + "box": true, + "min": 0 + }, + "SpanStartInteger": { + "type": "integer", + "box": true, + "min": 0 + }, + "String": { + "type": "string" + }, + "ThrottlingException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { + "shape": "String" + } + }, + "exception": true, + "retryable": { + "throttling": false + } + }, + "ValidationException": { + "type": "structure", + "required": ["message"], + "members": { + "message": { + "shape": "String" + } + }, + "exception": true + } + } +} diff --git a/lsp/server/aws-lsp-codewhisperer/src/index.ts b/lsp/server/aws-lsp-codewhisperer/src/index.ts new file mode 100644 index 00000000..dc493ad6 --- /dev/null +++ b/lsp/server/aws-lsp-codewhisperer/src/index.ts @@ -0,0 +1,2 @@ +export * from './language-server/codeWhispererServer' +export { CodeWhispererServiceProps, create as createCodeWhispererService } from './language-server/codeWhispererService' diff --git a/lsp/server/aws-lsp-codewhisperer/src/inline/futureProtocol.ts b/lsp/server/aws-lsp-codewhisperer/src/inline/futureProtocol.ts new file mode 100644 index 00000000..72279498 --- /dev/null +++ b/lsp/server/aws-lsp-codewhisperer/src/inline/futureProtocol.ts @@ -0,0 +1,44 @@ +import { + ProtocolRequestType, + StaticRegistrationOptions, + TextDocumentPositionParams, + TextDocumentRegistrationOptions, + WorkDoneProgressOptions, + WorkDoneProgressParams, +} from 'vscode-languageserver' +import { InlineCompletionContext, InlineCompletionItem, InlineCompletionList } from './futureTypes' + +/** + * Inline completion is not a part of the language server protocol. + * It is being proposed at this time (https://github.com/microsoft/language-server-protocol/pull/1673). + * + * This file contains boilerplate code that goes away if that proposal goes mainline, as + * it would be a part of the `vscode-languageserver` package. + * + * The expectation would be that when inline completion becomes part of the + * protocol standard, we have a low-friction transition, since there shouldn't be much drift + * between these types, and the final standard's types. + */ + +type InlineCompletionOptions = WorkDoneProgressOptions + +type InlineCompletionRegistrationOptions = InlineCompletionOptions & + TextDocumentRegistrationOptions & + StaticRegistrationOptions + +export type InlineCompletionParams = WorkDoneProgressParams & + TextDocumentPositionParams & { + context: InlineCompletionContext + } + +/** + * inlineCompletionRequestType defines the custom method that the language client + * requests from the server to provide inline completion recommendations. + */ +export const inlineCompletionRequestType = new ProtocolRequestType< + InlineCompletionParams, + InlineCompletionList | InlineCompletionItem[] | null, + InlineCompletionItem[], + void, + InlineCompletionRegistrationOptions +>('aws/textDocument/inlineCompletion') diff --git a/lsp/server/aws-lsp-codewhisperer/src/inline/futureTypes.ts b/lsp/server/aws-lsp-codewhisperer/src/inline/futureTypes.ts new file mode 100644 index 00000000..e501bf64 --- /dev/null +++ b/lsp/server/aws-lsp-codewhisperer/src/inline/futureTypes.ts @@ -0,0 +1,175 @@ +import { Command } from 'vscode-languageserver' +import { Range } from 'vscode-languageserver-textdocument' + +/** + * Inline completion is not a part of the language server protocol. + * It is being proposed at this time (https://github.com/microsoft/language-server-protocol/pull/1673). + * + * This file contains boilerplate code that goes away if that proposal goes mainline, as + * it would be a part of the `vscode-languageserver` package. + * + * The expectation would be that when inline completion becomes part of the + * protocol standard, we have a low-friction transition, since there shouldn't be much drift + * between these types, and the final standard's types. + * + * This file contains types defined as part of the proposal. + * They have been copied from https://github.com/microsoft/vscode-languageserver-node/pull/1190 + * Relating to code copied from the proposal PR... (via https://github.com/microsoft/vscode-languageserver-node/blob/main/License.txt) +Copyright (c) Microsoft Corporation + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * A string value used as a snippet is a template which allows to insert text + * and to control the editor cursor when insertion happens. + * + * A snippet can define tab stops and placeholders with `$1`, `$2` + * and `${3:foo}`. `$0` defines the final tab stop, it defaults to + * the end of the snippet. Variables are defined with `$name` and + * `${name:default value}`. + */ +export interface StringValue { + /** + * The kind of string value. + */ + kind: 'snippet' + /** + * The snippet string. + */ + value: string +} + +export namespace StringValue { + export function create(value: string): StringValue { + return { value, kind: 'snippet' } + } +} + +/** + * An inline completion item represents a text snippet that is proposed inline to complete text that is being typed. + */ +export interface InlineCompletionItem { + /** + * The text to replace the range with. Must be set. + */ + insertText: string | StringValue + + /** + * A text that is used to decide if this inline completion should be shown. When `falsy` the {@link InlineCompletionItem.insertText} is used. + */ + filterText?: string + + /** + * The range to replace. Must begin and end on the same line. + */ + range?: Range + + /** + * An optional {@link Command} that is executed *after* inserting this completion. + */ + command?: Command +} + +export namespace InlineCompletionItem { + export function create( + insertText: string | StringValue, + filterText?: string, + range?: Range, + command?: Command + ): InlineCompletionItem { + return { insertText, filterText, range, command } + } +} + +/** + * Represents a collection of {@link InlineCompletionItem inline completion items} to be presented in the editor. + */ +export interface InlineCompletionList { + /** + * The inline completion items + */ + items: InlineCompletionItem[] +} + +export namespace InlineCompletionList { + export function create(items: InlineCompletionItem[]): InlineCompletionList { + return { items } + } +} + +/** + * Describes how an {@link InlineCompletionItemProvider inline completion provider} was triggered. + */ +export namespace InlineCompletionTriggerKind { + /** + * Completion was triggered explicitly by a user gesture. + */ + export const Invoked: 0 = 0 + + /** + * Completion was triggered automatically while editing. + */ + export const Automatic: 1 = 1 +} + +export type InlineCompletionTriggerKind = 0 | 1 + +/** + * Describes the currently selected completion item. + */ +export interface SelectedCompletionInfo { + /** + * The range that will be replaced if this completion item is accepted. + */ + range: Range + + /** + * The text the range will be replaced with if this completion is accepted. + */ + text: string +} + +export namespace SelectedCompletionInfo { + export function create(range: Range, text: string): SelectedCompletionInfo { + return { range, text } + } +} + +/** + * Provides information about the context in which an inline completion was requested. + */ +export interface InlineCompletionContext { + /** + * Describes how the inline completion was triggered. + */ + triggerKind: InlineCompletionTriggerKind + + /** + * Provides information about the currently selected item in the autocomplete widget if it is visible. + */ + selectedCompletionInfo?: SelectedCompletionInfo +} + +export namespace InlineCompletionContext { + export function create( + triggerKind: InlineCompletionTriggerKind, + selectedCompletionInfo?: SelectedCompletionInfo + ): InlineCompletionContext { + return { triggerKind, selectedCompletionInfo } + } +} diff --git a/lsp/server/aws-lsp-codewhisperer/src/language-server/codeWhispererServer.ts b/lsp/server/aws-lsp-codewhisperer/src/language-server/codeWhispererServer.ts new file mode 100644 index 00000000..30c5e1f3 --- /dev/null +++ b/lsp/server/aws-lsp-codewhisperer/src/language-server/codeWhispererServer.ts @@ -0,0 +1,122 @@ +import { AwsLanguageService } from '@lsp-placeholder/aws-lsp-core' +import { + CancellationToken, + CompletionParams, + Connection, + InitializeParams, + InitializeResult, + TextDocumentSyncKind, + TextDocuments, +} from 'vscode-languageserver' +import { TextDocument } from 'vscode-languageserver-textdocument' +import { InlineCompletionParams, inlineCompletionRequestType } from '../inline/futureProtocol' +import { InlineCompletionList } from '../inline/futureTypes' +import { CodeWhispererService } from './codeWhispererService' + +export type CodeWhispererServerProps = { + connection: Connection + codeWhispererService: AwsLanguageService +} + +/** + * This is a demonstration language server that gets code recommendations from + * CodeWisperer. + * + * It will be used to explore deeper integration concerns, like providing bearer token access. + */ +export class CodeWhispererServer { + public static readonly serverId = 'aws-lsp-codewhisperer' + + protected documents = new TextDocuments(TextDocument) + + // HACK: Ideally we keep things to the standard AwsLanguageService interface + // For now we're experimenting with service calls. + protected service: CodeWhispererService + + protected connection: Connection + + constructor(private readonly props: CodeWhispererServerProps) { + this.connection = props.connection + + this.service = this.props.codeWhispererService as CodeWhispererService + + this.connection.onInitialize((params: InitializeParams) => { + // this.options = params; + const result: InitializeResult = { + // serverInfo: initialisationOptions?.serverInfo, + capabilities: { + textDocumentSync: { + openClose: true, + change: TextDocumentSyncKind.Incremental, + }, + completionProvider: { + resolveProvider: true, + completionItem: { + labelDetailsSupport: true, + }, + }, + }, + } + return result + }) + this.registerHandlers() + this.documents.listen(this.connection) + this.connection.listen() + + this.connection.console.info('AWS CodeWhisperer language server started!') + } + + getTextDocument(uri: string): TextDocument { + const textDocument = this.documents.get(uri) + + if (!textDocument) { + throw new Error(`Document with uri ${uri} not found.`) + } + + return textDocument + } + + registerHandlers() { + // TODO : Design Note: inline completions are not a part of the LSP spec, so + // we define our own (with message name aws/textDocument/inlineCompletion). + // This implementation follows the proposal + // in https://github.com/microsoft/language-server-protocol/pull/1673 (protocol) + // and https://github.com/microsoft/vscode-languageserver-node/pull/1190 (vscode-languageserver-node libraries) + // If/when it is added to the spec, we can transition this code over the spec, + // release servers with new major versions, and update the Toolkit clients appropriately. + this.connection.onRequest(inlineCompletionRequestType, async (params: InlineCompletionParams, token) => { + const results: InlineCompletionList = { + items: [], + } + params.context + + const textDocument = this.getTextDocument(params.textDocument.uri) + if (this.service.isSupported(textDocument)) { + return await this.service.doInlineCompletion({ + textDocument, + position: params.position, + context: params.context, + token, + }) + } + + return results + }) + + this.connection.onCompletion(async (params: CompletionParams, token: CancellationToken) => { + const textDocument = this.getTextDocument(params.textDocument.uri) + + if (this.service.isSupported(textDocument)) { + return await this.service.doComplete2({ + textDocument, + position: params.position, + token, + }) + } + + return + }) + + this.connection.onCompletionResolve(item => item) + } +} diff --git a/lsp/server/aws-lsp-codewhisperer/src/language-server/codeWhispererService.ts b/lsp/server/aws-lsp-codewhisperer/src/language-server/codeWhispererService.ts new file mode 100644 index 00000000..03137a0a --- /dev/null +++ b/lsp/server/aws-lsp-codewhisperer/src/language-server/codeWhispererService.ts @@ -0,0 +1,183 @@ +import { AwsLanguageService } from '@lsp-placeholder/aws-lsp-core' +import { ServiceConfigurationOptions } from 'aws-sdk/lib/service' +import { CancellationToken, CompletionItem, CompletionItemKind, Connection } from 'vscode-languageserver' +import { Position, Range, TextDocument, TextEdit } from 'vscode-languageserver-textdocument' +import { CompletionList, Diagnostic, FormattingOptions, Hover } from 'vscode-languageserver-types' +import { createCodeWhispererClient } from '../client/codewhisperer' +import { + InlineCompletionContext, + InlineCompletionItem, + InlineCompletionList, + InlineCompletionTriggerKind, +} from '../inline/futureTypes' +import CodeWhispererClient = require('../client/codewhispererclient') + +export type CodeWhispererServiceProps = { + displayName: string + // HACK: connection is passed in for logging purposes + connection: Connection +} + +export interface CompletionParams { + textDocument: TextDocument + position: Position + token: CancellationToken +} + +interface DoInlineCompletionParams { + textDocument: TextDocument + position: Position + context: InlineCompletionContext + token: CancellationToken +} + +interface GetRcommendationsParams { + textDocument: TextDocument + position: Position + maxResults: number + token: CancellationToken +} + +export class CodeWhispererService implements AwsLanguageService { + constructor(private readonly props: CodeWhispererServiceProps) {} + + isSupported(document: TextDocument): boolean { + return true + } + + async doComplete(textDocument: TextDocument, position: Position): Promise { + return this.doComplete2({ textDocument, position, token: CancellationToken.None }) + } + + // TODO : Design notes : We may want to change the AwsLanguageService signatures + // to provide more details coming in through the LSP event. + // In this case, we also want access to the cancellation token. + async doComplete2(params: CompletionParams): Promise { + const recommendations = await this.getRecommendations({ + textDocument: params.textDocument, + position: params.position, + maxResults: 5, + token: params.token, + }) + + let count = 1 + let items: CompletionItem[] = recommendations.map(r => { + const itemId = count++ + + return { + // We don't just stick the recommendation into the completion list, + // because multi-line recommendations don't render nicely. + // Lean into the "documentation" instead to show the "preview" + // label: r.content, + label: `CodeWhisperer Recommendation`, + insertText: r.content, + labelDetails: { + description: 'CodeWhisperer', + detail: ` (${itemId})`, + }, + documentation: r.content, + kind: CompletionItemKind.Snippet, + filterText: 'aaa CodeWhisperer', + } + }) + + const completions: CompletionList = { + isIncomplete: false, + items, + } + + return completions + } + + // TODO : Design notes : What would the AwsLanguageService signature look like? + async doInlineCompletion(params: DoInlineCompletionParams): Promise { + const recommendations = await this.getRecommendations({ + textDocument: params.textDocument, + position: params.position, + maxResults: params.context.triggerKind == InlineCompletionTriggerKind.Automatic ? 1 : 5, + token: params.token, + }) + + let items: InlineCompletionItem[] = recommendations.map(r => { + return { + insertText: r.content, + range: params.context.selectedCompletionInfo?.range, + } + }) + + const completions: InlineCompletionList = { + items, + } + + return completions + } + + private async getRecommendations(params: GetRcommendationsParams): Promise { + // This just uses the system's default credentials. A future investigation will + // look at providing credentials from the LSP client. + const options: ServiceConfigurationOptions = { + region: 'us-east-1', + } + + // CONCEPT: This is using the IAM credentials client. + // We want to build up to using the bearer token client. + const client = createCodeWhispererClient(options) + + const left = params.textDocument.getText({ + start: { line: 0, character: 0 }, + end: params.position, + }) + const right = params.textDocument.getText({ + start: params.position, + end: params.textDocument.positionAt(params.textDocument.getText().length), + }) + + const request: CodeWhispererClient.GenerateRecommendationsRequest = { + fileContext: { + filename: params.textDocument.uri, + programmingLanguage: { + languageName: 'typescript', + }, + leftFileContent: left, + rightFileContent: right, + }, + maxResults: params.maxResults, + } + + const results: CodeWhispererClient.Recommendation[] = [] + + // We will get all the paginated recommendations. + // This is slow, and holds up the IDE's autocompletion list from showing. + // We wouldn't do this in a release. + do { + if (params.token.isCancellationRequested) { + this.props.connection.console.info('*** CANCELLED ***') + return [] + } + + const response = await client.generateRecommendations(request).promise() + + request.nextToken = response.nextToken + + if (response.recommendations) { + results.push(...response.recommendations) + } + } while (request.nextToken !== undefined && request.nextToken !== '' && results.length < params.maxResults) + + return results + } + + doValidation(textDocument: TextDocument): PromiseLike { + throw new Error('Method not implemented.') + } + doHover(textDocument: TextDocument, position: Position): PromiseLike { + throw new Error('Method not implemented.') + } + format(textDocument: TextDocument, range: Range, options: FormattingOptions): TextEdit[] { + throw new Error('Method not implemented.') + } +} + +export function create(props: CodeWhispererServiceProps): AwsLanguageService { + return new CodeWhispererService(props) +} diff --git a/lsp/server/aws-lsp-codewhisperer/tsconfig.json b/lsp/server/aws-lsp-codewhisperer/tsconfig.json new file mode 100644 index 00000000..0b9cfe9b --- /dev/null +++ b/lsp/server/aws-lsp-codewhisperer/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.packages.json", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./out" + }, + "include": ["src"] +} diff --git a/lsp/tsconfig.json b/lsp/tsconfig.json index 382df0c1..9be82d2a 100644 --- a/lsp/tsconfig.json +++ b/lsp/tsconfig.json @@ -30,6 +30,9 @@ { "path": "./server/aws-lsp-cloudformation" }, + { + "path": "./server/aws-lsp-codewhisperer" + }, { "path": "./server/aws-lsp-s3" }, @@ -39,6 +42,9 @@ { "path": "./app/aws-lsp-cloudformation-binary" }, + { + "path": "./app/aws-lsp-codewhisperer-binary" + }, { "path": "./app/aws-lsp-s3-binary" }