diff options
Diffstat (limited to 'extension')
35 files changed, 804 insertions, 699 deletions
diff --git a/extension/package-lock.json b/extension/package-lock.json index 79b010cc..88cad6c6 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.60", + "version": "0.0.73", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.60", + "version": "0.0.73", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index 63a49173..ceba8698 100644 --- a/extension/package.json +++ b/extension/package.json @@ -14,7 +14,7 @@ "displayName": "Continue", "pricing": "Free", "description": "Accelerating software development with language models", - "version": "0.0.60", + "version": "0.0.73", "publisher": "Continue", "engines": { "vscode": "^1.74.0" diff --git a/extension/react-app/package-lock.json b/extension/react-app/package-lock.json index db15fd2b..85b8633b 100644 --- a/extension/react-app/package-lock.json +++ b/extension/react-app/package-lock.json @@ -12,17 +12,20 @@ "@types/vscode-webview": "^1.57.1", "downshift": "^7.6.0", "posthog-js": "^1.58.0", + "prismjs": "^1.29.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-markdown": "^8.0.5", "react-redux": "^8.0.5", "react-switch": "^7.0.0", + "react-syntax-highlighter": "^15.5.0", "styled-components": "^5.3.6", "vscode-webview": "^1.0.1-beta.1" }, "devDependencies": { "@types/react": "^18.0.27", "@types/react-dom": "^18.0.10", + "@types/react-syntax-highlighter": "^15.5.7", "@types/styled-components": "^5.1.26", "@vitejs/plugin-react-swc": "^3.0.0", "autoprefixer": "^10.4.13", @@ -952,6 +955,15 @@ "@types/react": "*" } }, + "node_modules/@types/react-syntax-highlighter": { + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-15.5.7.tgz", + "integrity": "sha512-bo5fEO5toQeyCp0zVHBeggclqf5SQ/Z5blfFmjwO5dkMVGPgmiwZsJh9nu/Bo5L7IHTuGWrja6LxJVE2uB5ZrQ==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/scheduler": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", @@ -1223,6 +1235,24 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -1534,6 +1564,18 @@ "reusify": "^1.0.4" } }, + "node_modules/fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/fflate": { "version": "0.4.8", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", @@ -1551,6 +1593,14 @@ "node": ">=8" } }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/fraction.js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", @@ -1642,6 +1692,15 @@ "node": ">=4" } }, + "node_modules/hast-util-parse-selector": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", + "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-whitespace": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz", @@ -1651,6 +1710,60 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hastscript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", + "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "dependencies": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^1.0.0", + "hast-util-parse-selector": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript/node_modules/comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hastscript/node_modules/property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", + "dependencies": { + "xtend": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hastscript/node_modules/space-separated-tokens": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "engines": { + "node": "*" + } + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -1669,6 +1782,28 @@ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" }, + "node_modules/is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "dependencies": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -1715,6 +1850,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1736,6 +1880,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -1816,6 +1969,19 @@ "loose-envify": "cli.js" } }, + "node_modules/lowlight": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "dependencies": { + "fault": "^1.0.0", + "highlight.js": "~10.7.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/mdast-util-definitions": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", @@ -2403,6 +2569,32 @@ "node": ">= 6" } }, + "node_modules/parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "dependencies": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -2570,6 +2762,14 @@ "rrweb-snapshot": "^1.1.14" } }, + "node_modules/prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -2734,6 +2934,21 @@ "react-dom": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-syntax-highlighter": { + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz", + "integrity": "sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "highlight.js": "^10.4.1", + "lowlight": "^1.17.0", + "prismjs": "^1.27.0", + "refractor": "^3.6.0" + }, + "peerDependencies": { + "react": ">= 0.14.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -2755,6 +2970,28 @@ "node": ">=8.10.0" } }, + "node_modules/refractor": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz", + "integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==", + "dependencies": { + "hastscript": "^6.0.0", + "parse-entities": "^2.0.0", + "prismjs": "~1.27.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/prismjs": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz", + "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==", + "engines": { + "node": ">=6" + } + }, "node_modules/regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", @@ -3304,7 +3541,6 @@ "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" } @@ -3859,6 +4095,15 @@ "@types/react": "*" } }, + "@types/react-syntax-highlighter": { + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-15.5.7.tgz", + "integrity": "sha512-bo5fEO5toQeyCp0zVHBeggclqf5SQ/Z5blfFmjwO5dkMVGPgmiwZsJh9nu/Bo5L7IHTuGWrja6LxJVE2uB5ZrQ==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/scheduler": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", @@ -4041,6 +4286,16 @@ "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==" }, + "character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==" + }, + "character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==" + }, "chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -4284,6 +4539,14 @@ "reusify": "^1.0.4" } }, + "fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "requires": { + "format": "^0.2.0" + } + }, "fflate": { "version": "0.4.8", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", @@ -4298,6 +4561,11 @@ "to-regex-range": "^5.0.1" } }, + "format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==" + }, "fraction.js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", @@ -4360,11 +4628,53 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" }, + "hast-util-parse-selector": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", + "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==" + }, "hast-util-whitespace": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz", "integrity": "sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==" }, + "hastscript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", + "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "requires": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^1.0.0", + "hast-util-parse-selector": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0" + }, + "dependencies": { + "comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==" + }, + "property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", + "requires": { + "xtend": "^4.0.0" + } + }, + "space-separated-tokens": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==" + } + } + }, + "highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==" + }, "hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -4385,6 +4695,20 @@ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" }, + "is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==" + }, + "is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "requires": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + } + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -4408,6 +4732,11 @@ "has": "^1.0.3" } }, + "is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==" + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -4423,6 +4752,11 @@ "is-extglob": "^2.1.1" } }, + "is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==" + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -4477,6 +4811,15 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, + "lowlight": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "requires": { + "fault": "^1.0.0", + "highlight.js": "~10.7.0" + } + }, "mdast-util-definitions": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", @@ -4808,6 +5151,26 @@ "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", "dev": true }, + "parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "requires": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + }, + "dependencies": { + "character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==" + } + } + }, "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -4905,6 +5268,11 @@ "rrweb-snapshot": "^1.1.14" } }, + "prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==" + }, "prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5004,6 +5372,18 @@ "prop-types": "^15.7.2" } }, + "react-syntax-highlighter": { + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz", + "integrity": "sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==", + "requires": { + "@babel/runtime": "^7.3.1", + "highlight.js": "^10.4.1", + "lowlight": "^1.17.0", + "prismjs": "^1.27.0", + "refractor": "^3.6.0" + } + }, "read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -5022,6 +5402,23 @@ "picomatch": "^2.2.1" } }, + "refractor": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz", + "integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==", + "requires": { + "hastscript": "^6.0.0", + "parse-entities": "^2.0.0", + "prismjs": "~1.27.0" + }, + "dependencies": { + "prismjs": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz", + "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==" + } + } + }, "regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", @@ -5369,8 +5766,7 @@ "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "yaml": { "version": "1.10.2", diff --git a/extension/react-app/package.json b/extension/react-app/package.json index 230bd5b9..78dd12e4 100644 --- a/extension/react-app/package.json +++ b/extension/react-app/package.json @@ -13,17 +13,20 @@ "@types/vscode-webview": "^1.57.1", "downshift": "^7.6.0", "posthog-js": "^1.58.0", + "prismjs": "^1.29.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-markdown": "^8.0.5", "react-redux": "^8.0.5", "react-switch": "^7.0.0", + "react-syntax-highlighter": "^15.5.0", "styled-components": "^5.3.6", "vscode-webview": "^1.0.1-beta.1" }, "devDependencies": { "@types/react": "^18.0.27", "@types/react-dom": "^18.0.10", + "@types/react-syntax-highlighter": "^15.5.7", "@types/styled-components": "^5.1.26", "@vitejs/plugin-react-swc": "^3.0.0", "autoprefixer": "^10.4.13", diff --git a/extension/react-app/public/continue.gif b/extension/react-app/public/continue.gif Binary files differnew file mode 100644 index 00000000..daed6663 --- /dev/null +++ b/extension/react-app/public/continue.gif diff --git a/extension/react-app/src/components/CodeBlock.tsx b/extension/react-app/src/components/CodeBlock.tsx index 0aae0bbb..b0de13ac 100644 --- a/extension/react-app/src/components/CodeBlock.tsx +++ b/extension/react-app/src/components/CodeBlock.tsx @@ -5,28 +5,27 @@ import { defaultBorderRadius, secondaryDark, vscBackground } from "."; import { Clipboard, CheckCircle } from "@styled-icons/heroicons-outline"; +import StyledCode from "./StyledCode"; + const StyledPre = styled.pre` overflow-y: scroll; word-wrap: normal; border: 0.5px solid gray; border-radius: ${defaultBorderRadius}; background-color: ${secondaryDark}; - padding: 8px; - padding-top: 14px; - padding-bottom: 16px; + position: relative; `; -const StyledCode = styled.code``; +const CopyButtonDiv = styled.div` + position: absolute; + top: 4px; + right: 4px; +`; const StyledCopyButton = styled.button<{ visible: boolean }>` - /* position: relative; */ - float: right; border: none; - background-color: ${secondaryDark}; + background-color: transparent; cursor: pointer; - padding: 0; - /* margin: 4px; */ - margin-top: -6px; visibility: ${(props) => (props.visible ? "visible" : "hidden")}; `; @@ -62,9 +61,55 @@ function CopyButton(props: { textToCopy: string; visible: boolean }) { ); } -function CodeBlock(props: { language?: string; children: string }) { +function CodeBlock(props: { children: string }) { + const [result, setResult] = useState<AutoHighlightResult | undefined>( + undefined + ); useEffect(() => { - hljs.highlightAll(); + const result = hljs.highlightAuto( + (props.children as any).props.children[0], + [ + "python", + "javascript", + "typescript", + "bash", + "html", + "css", + "json", + "yaml", + "markdown", + "sql", + "java", + "c", + "cpp", + "csharp", + "go", + "kotlin", + "php", + "ruby", + "rust", + "scala", + "swift", + "dart", + "haskell", + "perl", + "r", + "matlab", + "powershell", + "lua", + "elixir", + "clojure", + "groovy", + "julia", + "vbnet", + "objectivec", + "fsharp", + "erlang", + "ocaml", + ] + ); + console.log(result); + setResult(result); }, [props.children]); const [hovered, setHovered] = useState<boolean>(false); @@ -77,11 +122,15 @@ function CodeBlock(props: { language?: string; children: string }) { setHovered(false); }} > - <CopyButton - visible={hovered} - textToCopy={(props.children as any).props.children[0]} - /> - <StyledCode>{props.children}</StyledCode> + <CopyButtonDiv> + <CopyButton + visible={hovered} + textToCopy={(props.children as any).props.children[0]} + /> + </CopyButtonDiv> + <StyledCode language={result?.language}> + {(props.children as any).props.children[0]} + </StyledCode> </StyledPre> ); } diff --git a/extension/react-app/src/components/ContinueButton.tsx b/extension/react-app/src/components/ContinueButton.tsx index c6117bf9..ef6719b7 100644 --- a/extension/react-app/src/components/ContinueButton.tsx +++ b/extension/react-app/src/components/ContinueButton.tsx @@ -1,6 +1,8 @@ import styled, { keyframes } from "styled-components"; import { Button } from "."; import { Play } from "@styled-icons/heroicons-outline"; +import { useSelector } from "react-redux"; +import { RootStore } from "../redux/store"; let StyledButton = styled(Button)` margin: auto; @@ -25,14 +27,21 @@ let StyledButton = styled(Button)` `; function ContinueButton(props: { onClick?: () => void; hidden?: boolean }) { + const vscMediaUrl = useSelector( + (state: RootStore) => state.config.vscMediaUrl + ); + return ( <StyledButton hidden={props.hidden} className="m-auto" onClick={props.onClick} > - <Play /> - {/* <img src={"/continue_arrow.png"} width="16px"></img> */} + {vscMediaUrl ? ( + <img src={`${vscMediaUrl}/play_button.png`} width="22px" /> + ) : ( + <Play /> + )} Continue </StyledButton> ); diff --git a/extension/react-app/src/components/DebugPanel.tsx b/extension/react-app/src/components/DebugPanel.tsx index 30f38779..94dbac9e 100644 --- a/extension/react-app/src/components/DebugPanel.tsx +++ b/extension/react-app/src/components/DebugPanel.tsx @@ -6,6 +6,7 @@ import { setApiUrl, setVscMachineId, setSessionId, + setVscMediaUrl, } from "../redux/slices/configSlice"; import { setHighlightedCode } from "../redux/slices/miscSlice"; import { updateFileSystem } from "../redux/slices/debugContexSlice"; @@ -37,6 +38,7 @@ function DebugPanel(props: DebugPanelProps) { dispatch(setApiUrl(event.data.apiUrl)); dispatch(setVscMachineId(event.data.vscMachineId)); dispatch(setSessionId(event.data.sessionId)); + dispatch(setVscMediaUrl(event.data.vscMediaUrl)); break; case "highlightedCode": dispatch(setHighlightedCode(event.data.rangeInFile)); diff --git a/extension/react-app/src/components/HeaderButtonWithText.tsx b/extension/react-app/src/components/HeaderButtonWithText.tsx index acaca9ce..30931f86 100644 --- a/extension/react-app/src/components/HeaderButtonWithText.tsx +++ b/extension/react-app/src/components/HeaderButtonWithText.tsx @@ -6,14 +6,20 @@ interface HeaderButtonWithTextProps { text: string; onClick?: (e: any) => void; children: React.ReactNode; + disabled?: boolean; } const HeaderButtonWithText = (props: HeaderButtonWithTextProps) => { const [hover, setHover] = useState(false); return ( <HeaderButton + disabled={props.disabled} style={{ padding: "1px", paddingLeft: hover ? "4px" : "1px" }} - onMouseEnter={() => setHover(true)} + onMouseEnter={() => { + if (!props.disabled) { + setHover(true); + } + }} onMouseLeave={() => { setHover(false); }} diff --git a/extension/react-app/src/components/LoadingCover.tsx b/extension/react-app/src/components/LoadingCover.tsx new file mode 100644 index 00000000..a0f8f7a2 --- /dev/null +++ b/extension/react-app/src/components/LoadingCover.tsx @@ -0,0 +1,50 @@ +import React from "react"; +import styled from "styled-components"; + +const StyledDiv = styled.div` + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100vh; + background: linear-gradient( + 101.79deg, + #12887a 0%, + #87245c 32%, + #e12637 63%, + #ffb215 100% + ); + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + z-index: 10; +`; + +const StyledImg = styled.img` + /* add your styles here */ +`; + +const StyledDiv2 = styled.div` + width: 50%; + height: 5px; + background: white; + margin-top: 20px; +`; + +interface LoadingCoverProps { + message: string; + hidden?: boolean; +} + +const LoadingCover = (props: LoadingCoverProps) => { + return ( + <StyledDiv style={{ display: props.hidden ? "none" : "inherit" }}> + <StyledImg src="continue.gif" alt="centered image" width="50%" /> + <StyledDiv2></StyledDiv2> + <p>{props.message}</p> + </StyledDiv> + ); +}; + +export default LoadingCover; diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx index 74a1c4e8..827d2d5f 100644 --- a/extension/react-app/src/components/StepContainer.tsx +++ b/extension/react-app/src/components/StepContainer.tsx @@ -200,6 +200,7 @@ function StepContainer(props: StepContainerProps) { <> <HeaderButtonWithText + disabled={props.historyNode.active as boolean} onClick={(e) => { e.stopPropagation(); props.onDelete(); diff --git a/extension/react-app/src/components/StyledCode.tsx b/extension/react-app/src/components/StyledCode.tsx new file mode 100644 index 00000000..c5ed0101 --- /dev/null +++ b/extension/react-app/src/components/StyledCode.tsx @@ -0,0 +1,19 @@ +import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; +import { vscDarkPlus as highlightStyle } from "react-syntax-highlighter/dist/esm/styles/prism"; + +interface StyledCodeProps { + children: string; + language?: string; +} + +const StyledCode = (props: StyledCodeProps) => ( + <SyntaxHighlighter + customStyle={{ margin: "0" }} + style={highlightStyle} + language={props.language || "python"} + > + {props.children} + </SyntaxHighlighter> +); + +export default StyledCode; diff --git a/extension/react-app/src/components/TextDialog.tsx b/extension/react-app/src/components/TextDialog.tsx index e50a7686..2632e572 100644 --- a/extension/react-app/src/components/TextDialog.tsx +++ b/extension/react-app/src/components/TextDialog.tsx @@ -15,6 +15,7 @@ const DialogContainer = styled.div` top: 50%; left: 50%; transform: translate(-50%, -50%); + width: 75%; `; const Dialog = styled.div` @@ -76,7 +77,6 @@ const TextDialog = (props: { <Dialog> <P>Thanks for your feedback. We'll get back to you soon!</P> <TextArea - cols={50} rows={10} ref={textAreaRef} onKeyDown={(e) => { diff --git a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts index 824bb086..3d8e0a38 100644 --- a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts +++ b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts @@ -13,6 +13,8 @@ abstract class AbstractContinueGUIClientProtocol { callback: (commands: { name: string; description: string }[]) => void ): void; + abstract changeDefaultModel(model: string): void; + abstract sendClear(): void; abstract retryAtIndex(index: number): void; diff --git a/extension/react-app/src/hooks/useContinueGUIProtocol.ts b/extension/react-app/src/hooks/useContinueGUIProtocol.ts index 59397742..f43a66ff 100644 --- a/extension/react-app/src/hooks/useContinueGUIProtocol.ts +++ b/extension/react-app/src/hooks/useContinueGUIProtocol.ts @@ -55,6 +55,10 @@ class ContinueGUIClientProtocol extends AbstractContinueGUIClientProtocol { }); } + changeDefaultModel(model: string) { + this.messenger.send("change_default_model", { model }); + } + sendClear() { this.messenger.send("clear_history", {}); } diff --git a/extension/react-app/src/redux/slices/configSlice.ts b/extension/react-app/src/redux/slices/configSlice.ts index a6a641e6..1b107bed 100644 --- a/extension/react-app/src/redux/slices/configSlice.ts +++ b/extension/react-app/src/redux/slices/configSlice.ts @@ -37,9 +37,21 @@ export const configSlice = createSlice({ ...state, sessionId: action.payload, }), + setVscMediaUrl: ( + state: RootStore["config"], + action: { type: string; payload: string } + ) => ({ + ...state, + vscMediaUrl: action.payload, + }), }, }); -export const { setVscMachineId, setApiUrl, setWorkspacePath, setSessionId } = - configSlice.actions; +export const { + setVscMachineId, + setApiUrl, + setWorkspacePath, + setSessionId, + setVscMediaUrl, +} = configSlice.actions; export default configSlice.reducer; diff --git a/extension/react-app/src/redux/store.ts b/extension/react-app/src/redux/store.ts index f9eb0517..a5eef4ba 100644 --- a/extension/react-app/src/redux/store.ts +++ b/extension/react-app/src/redux/store.ts @@ -21,6 +21,7 @@ export interface RootStore { vscMachineId: string | undefined; sessionId: string | undefined; sessionStarted: number | undefined; + vscMediaUrl: string | undefined; }; chat: { messages: ChatMessage[]; diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx index f0e3ffd4..13b74423 100644 --- a/extension/react-app/src/tabs/gui.tsx +++ b/extension/react-app/src/tabs/gui.tsx @@ -18,6 +18,7 @@ import ReactSwitch from "react-switch"; import { usePostHog } from "posthog-js/react"; import { useSelector } from "react-redux"; import { RootStore } from "../redux/store"; +import LoadingCover from "../components/LoadingCover"; const TopGUIDiv = styled.div` overflow: hidden; @@ -215,7 +216,7 @@ function GUI(props: GUIProps) { useEffect(() => { const listener = (e: any) => { // Cmd + J to toggle fast model - if (e.key === "j" && e.metaKey) { + if (e.key === "i" && e.metaKey && e.shiftKey) { setUsingFastModel((prev) => !prev); } }; @@ -230,21 +231,24 @@ function GUI(props: GUIProps) { console.log("CLIENT ON STATE UPDATE: ", client, client?.onStateUpdate); client?.onStateUpdate((state) => { // Scroll only if user is at very bottom of the window. + setUsingFastModel(state.default_model === "gpt-3.5-turbo"); const shouldScrollToBottom = topGuiDivRef.current && topGuiDivRef.current?.offsetHeight - window.scrollY < 100; setWaitingForSteps(state.active); setHistory(state.history); setUserInputQueue(state.user_input_queue); - const nextStepsOpen = [...stepsOpen]; - for ( - let i = nextStepsOpen.length; - i < state.history.timeline.length; - i++ - ) { - nextStepsOpen.push(true); - } - setStepsOpen(nextStepsOpen); + setStepsOpen((prev) => { + const nextStepsOpen = [...prev]; + for ( + let i = nextStepsOpen.length; + i < state.history.timeline.length; + i++ + ) { + nextStepsOpen.push(true); + } + return nextStepsOpen; + }); if (shouldScrollToBottom) { scrollToBottom(); @@ -325,6 +329,7 @@ function GUI(props: GUIProps) { // const iterations = useSelector(selectIterations); return ( <> + <LoadingCover hidden={true} message="Downloading local model..." /> <TextDialog showDialog={showFeedbackDialog} onEnter={(text) => { @@ -347,9 +352,7 @@ function GUI(props: GUIProps) { {typeof client === "undefined" && ( <> <Loader></Loader> - <p style={{ textAlign: "center" }}> - Trying to reconnect with server... - </p> + <p style={{ textAlign: "center" }}>Loading Continue server...</p> </> )} {history?.timeline.map((node: HistoryNode, index: number) => { @@ -388,7 +391,7 @@ function GUI(props: GUIProps) { /> ); })} - {/* {waitingForSteps && <Loader></Loader>} */} + {waitingForSteps && <Loader></Loader>} <div> {userInputQueue.map((input) => { @@ -426,9 +429,9 @@ function GUI(props: GUIProps) { }} hidden={!showDataSharingInfo} > - By turning on this switch, you signal that you would - contribute this software development data to a publicly - accessible, open-source dataset in the future. + By turning on this switch, you signal that you would contribute this + software development data to a publicly accessible, open-source dataset + in the future. <br /> <br /> <b> @@ -470,8 +473,11 @@ function GUI(props: GUIProps) { Contribute Data </span> </div> - {/* <HeaderButtonWithText + <HeaderButtonWithText onClick={() => { + client?.changeDefaultModel( + usingFastModel ? "gpt-4" : "gpt-3.5-turbo" + ); setUsingFastModel((prev) => !prev); }} text={usingFastModel ? "gpt-3.5-turbo" : "gpt-4"} @@ -481,7 +487,7 @@ function GUI(props: GUIProps) { > {usingFastModel ? "⚡" : "🧠"} </div> - </HeaderButtonWithText> */} + </HeaderButtonWithText> <HeaderButtonWithText onClick={() => { client?.sendClear(); diff --git a/extension/schema/History.d.ts b/extension/schema/History.d.ts index 508deaf0..6eb8ad81 100644 --- a/extension/schema/History.d.ts +++ b/extension/schema/History.d.ts @@ -8,8 +8,19 @@ export type History = History1; export type Name = string; export type Hide = boolean; +export type Description = string; export type SystemMessage = string; +export type Role = "assistant" | "user" | "system" | "function"; +export type Content = string; +export type Name1 = string; +export type Summary = string; +export type Name2 = string; +export type Arguments = string; +export type ChatContext = ChatMessage[]; +export type ManageOwnChatContext = boolean; export type Depth = number; +export type Deleted = boolean; +export type Active = boolean; export type Timeline = HistoryNode[]; export type CurrentIndex = number; @@ -28,12 +39,30 @@ export interface HistoryNode { step: Step; observation?: Observation; depth: Depth; + deleted?: Deleted; + active?: Active; [k: string]: unknown; } export interface Step { name?: Name; hide?: Hide; + description?: Description; system_message?: SystemMessage; + chat_context?: ChatContext; + manage_own_chat_context?: ManageOwnChatContext; + [k: string]: unknown; +} +export interface ChatMessage { + role: Role; + content?: Content; + name?: Name1; + summary: Summary; + function_call?: FunctionCall; + [k: string]: unknown; +} +export interface FunctionCall { + name: Name2; + arguments: Arguments; [k: string]: unknown; } export interface Observation { diff --git a/extension/schema/HistoryNode.d.ts b/extension/schema/HistoryNode.d.ts index c1507270..bc77be89 100644 --- a/extension/schema/HistoryNode.d.ts +++ b/extension/schema/HistoryNode.d.ts @@ -8,8 +8,19 @@ export type HistoryNode = HistoryNode1; export type Name = string; export type Hide = boolean; +export type Description = string; export type SystemMessage = string; +export type Role = "assistant" | "user" | "system" | "function"; +export type Content = string; +export type Name1 = string; +export type Summary = string; +export type Name2 = string; +export type Arguments = string; +export type ChatContext = ChatMessage[]; +export type ManageOwnChatContext = boolean; export type Depth = number; +export type Deleted = boolean; +export type Active = boolean; /** * A point in history, a list of which make up History @@ -18,12 +29,30 @@ export interface HistoryNode1 { step: Step; observation?: Observation; depth: Depth; + deleted?: Deleted; + active?: Active; [k: string]: unknown; } export interface Step { name?: Name; hide?: Hide; + description?: Description; system_message?: SystemMessage; + chat_context?: ChatContext; + manage_own_chat_context?: ManageOwnChatContext; + [k: string]: unknown; +} +export interface ChatMessage { + role: Role; + content?: Content; + name?: Name1; + summary: Summary; + function_call?: FunctionCall; + [k: string]: unknown; +} +export interface FunctionCall { + name: Name2; + arguments: Arguments; [k: string]: unknown; } export interface Observation { diff --git a/extension/scripts/.gitignore b/extension/scripts/.gitignore index 7af27c08..fbb3bf9f 100644 --- a/extension/scripts/.gitignore +++ b/extension/scripts/.gitignore @@ -1,3 +1,5 @@ testdb env -stdout.txt
\ No newline at end of file +stdout.txt +.continue_env_installed +**.whl
\ No newline at end of file diff --git a/extension/scripts/continuedev-0.1.1-py3-none-any.whl b/extension/scripts/continuedev-0.1.1-py3-none-any.whl Binary files differdeleted file mode 100644 index 99f96d80..00000000 --- a/extension/scripts/continuedev-0.1.1-py3-none-any.whl +++ /dev/null diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts index 32726c86..0c92f095 100644 --- a/extension/src/activation/activate.ts +++ b/extension/src/activation/activate.ts @@ -2,30 +2,31 @@ import * as vscode from "vscode"; import { registerAllCommands } from "../commands"; import { registerAllCodeLensProviders } from "../lang-server/codeLens"; import { sendTelemetryEvent, TelemetryEvent } from "../telemetry"; -import { getExtensionUri } from "../util/vscode"; -import * as path from "path"; // import { openCapturedTerminal } from "../terminal/terminalEmulator"; import IdeProtocolClient from "../continueIdeClient"; import { getContinueServerUrl } from "../bridge"; -import { setupDebugPanel, ContinueGUIWebviewViewProvider } from "../debugPanel"; import { CapturedTerminal } from "../terminal/terminalEmulator"; +import { setupDebugPanel, ContinueGUIWebviewViewProvider } from "../debugPanel"; +import { startContinuePythonServer } from "./environmentSetup"; +// import { CapturedTerminal } from "../terminal/terminalEmulator"; export let extensionContext: vscode.ExtensionContext | undefined = undefined; export let ideProtocolClient: IdeProtocolClient; -export function activateExtension( +export async function activateExtension( context: vscode.ExtensionContext, showTutorial: boolean ) { - sendTelemetryEvent(TelemetryEvent.ExtensionActivated); + extensionContext = context; + sendTelemetryEvent(TelemetryEvent.ExtensionActivated); registerAllCodeLensProviders(context); registerAllCommands(context); // vscode.window.registerWebviewViewProvider("continue.continueGUIView", setupDebugPanel); - - let serverUrl = getContinueServerUrl(); + await startContinuePythonServer(); + const serverUrl = getContinueServerUrl(); ideProtocolClient = new IdeProtocolClient( `${serverUrl.replace("http", "ws")}/ide/ws`, @@ -49,41 +50,8 @@ export function activateExtension( })(); // All opened terminals should be replaced by our own terminal - vscode.window.onDidOpenTerminal((terminal) => { - if (terminal.name === "Continue") { - return; - } - const options = terminal.creationOptions; - const capturedTerminal = new CapturedTerminal({ - ...options, - name: "Continue", - }); - terminal.dispose(); - if (!ideProtocolClient.continueTerminal) { - ideProtocolClient.continueTerminal = capturedTerminal; - } - }); + // vscode.window.onDidOpenTerminal((terminal) => {}); // If any terminals are open to start, replace them - vscode.window.terminals.forEach((terminal) => { - if (terminal.name === "Continue") { - return; - } - const options = terminal.creationOptions; - const capturedTerminal = new CapturedTerminal( - { - ...options, - name: "Continue", - }, - (commandOutput: string) => { - ideProtocolClient.sendCommandOutput(commandOutput); - } - ); - terminal.dispose(); - if (!ideProtocolClient.continueTerminal) { - ideProtocolClient.continueTerminal = capturedTerminal; - } - }); - - extensionContext = context; + // vscode.window.terminals.forEach((terminal) => {} } diff --git a/extension/src/activation/environmentSetup.ts b/extension/src/activation/environmentSetup.ts index cdaf3ac0..ec0e228d 100644 --- a/extension/src/activation/environmentSetup.ts +++ b/extension/src/activation/environmentSetup.ts @@ -7,6 +7,25 @@ import * as fs from "fs"; import rebuild from "@electron/rebuild"; import { getContinueServerUrl } from "../bridge"; import fetch from "node-fetch"; +import * as vscode from "vscode"; + +const MAX_RETRIES = 5; +async function retryThenFail( + fn: () => Promise<any>, + retries: number = MAX_RETRIES +): Promise<any> { + try { + return await fn(); + } catch (e) { + if (retries > 0) { + return await retryThenFail(fn, retries - 1); + } + vscode.window.showErrorMessage( + "Failed to set up Continue extension. Please email nate@continue.dev and we'll get this fixed ASAP!" + ); + throw e; + } +} async function runCommand(cmd: string): Promise<[string, string | undefined]> { console.log("Running command: ", cmd); @@ -52,14 +71,16 @@ async function getPythonPipCommands() { return [pythonCmd, pipCmd]; } -function getActivateUpgradeCommands(pythonCmd: string, pipCmd: string) { +function getActivateUpgradeTouchCommands(pythonCmd: string, pipCmd: string) { let activateCmd = ". env/bin/activate"; let pipUpgradeCmd = `${pipCmd} install --upgrade pip`; + let touchCmd = "touch .continue_env_installed"; if (process.platform == "win32") { activateCmd = ".\\env\\Scripts\\activate"; pipUpgradeCmd = `${pythonCmd} -m pip install --upgrade pip`; + touchCmd = "ni .continue_env_installed -type file"; } - return [activateCmd, pipUpgradeCmd]; + return [activateCmd, pipUpgradeCmd, touchCmd]; } function checkEnvExists() { @@ -77,55 +98,69 @@ function checkEnvExists() { ); } +function checkRequirementsInstalled() { + return fs.existsSync( + path.join(getExtensionUri().fsPath, "scripts", ".continue_env_installed") + ); +} + async function setupPythonEnv() { console.log("Setting up python env for Continue extension..."); - if (checkEnvExists()) return; - - // Assemble the command to create the env const [pythonCmd, pipCmd] = await getPythonPipCommands(); - const [activateCmd, pipUpgradeCmd] = getActivateUpgradeCommands( - pythonCmd, - pipCmd - ); - const createEnvCommand = [ - `cd ${path.join(getExtensionUri().fsPath, "scripts")}`, - `${pythonCmd} -m venv env`, - ].join("; "); + const [activateCmd, pipUpgradeCmd, touchCmd] = + getActivateUpgradeTouchCommands(pythonCmd, pipCmd); - // Repeat until it is successfully created (sometimes it fails to generate the bin, need to try again) - while (true) { - const [, stderr] = await runCommand(createEnvCommand); - if (stderr) { - throw new Error(stderr); - } - if (checkEnvExists()) { - break; - } else { - // Remove the env and try again - const removeCommand = `rm -rf ${path.join( - getExtensionUri().fsPath, - "scripts", - "env" - )}`; - await runCommand(removeCommand); + if (checkEnvExists()) { + console.log("Python env already exists, skipping..."); + } else { + // Assemble the command to create the env + const createEnvCommand = [ + `cd "${path.join(getExtensionUri().fsPath, "scripts")}"`, + `${pythonCmd} -m venv env`, + ].join(" ; "); + + // Repeat until it is successfully created (sometimes it fails to generate the bin, need to try again) + while (true) { + const [, stderr] = await runCommand(createEnvCommand); + if (stderr) { + throw new Error(stderr); + } + if (checkEnvExists()) { + break; + } else { + // Remove the env and try again + const removeCommand = `rm -rf "${path.join( + getExtensionUri().fsPath, + "scripts", + "env" + )}"`; + await runCommand(removeCommand); + } } + console.log( + "Successfully set up python env at ", + getExtensionUri().fsPath + "/scripts/env" + ); } - console.log( - "Successfully set up python env at ", - getExtensionUri().fsPath + "/scripts/env" - ); - const installRequirementsCommand = [ - `cd ${path.join(getExtensionUri().fsPath, "scripts")}`, - activateCmd, - pipUpgradeCmd, - `${pipCmd} install -r requirements.txt`, - ].join(" ; "); - const [, stderr] = await runCommand(installRequirementsCommand); - if (stderr) { - throw new Error(stderr); - } + await retryThenFail(async () => { + if (checkRequirementsInstalled()) { + console.log("Python requirements already installed, skipping..."); + } else { + const installRequirementsCommand = [ + `cd "${path.join(getExtensionUri().fsPath, "scripts")}"`, + activateCmd, + pipUpgradeCmd, + `${pipCmd} install -r requirements.txt`, + touchCmd, + ].join(" ; "); + const [, stderr] = await runCommand(installRequirementsCommand); + if (stderr) { + throw new Error(stderr); + } + } + }); } function readEnvFile(path: string) { @@ -180,15 +215,11 @@ export async function startContinuePythonServer() { await setupPythonEnv(); // Check vscode settings - let serverUrl = getContinueServerUrl(); + const serverUrl = getContinueServerUrl(); if (serverUrl !== "http://localhost:8000") { return; } - console.log("Starting Continue python server..."); - - if (await checkServerRunning(serverUrl)) return; - let activateCmd = ". env/bin/activate"; let pythonCmd = "python3"; if (process.platform == "win32") { @@ -196,38 +227,44 @@ export async function startContinuePythonServer() { pythonCmd = "python"; } - let command = `cd ${path.join( + let command = `cd "${path.join( getExtensionUri().fsPath, "scripts" - )} && ${activateCmd} && cd .. && ${pythonCmd} -m scripts.run_continue_server`; + )}" && ${activateCmd} && cd .. && ${pythonCmd} -m scripts.run_continue_server`; + + return await retryThenFail(async () => { + console.log("Starting Continue python server..."); - return new Promise(async (resolve, reject) => { - try { - const child = spawn(command, { - shell: true, - }); - child.stdout.on("data", (data: any) => { - console.log(`stdout: ${data}`); - }); - child.stderr.on("data", (data: any) => { - console.log(`stderr: ${data}`); - if (data.includes("Uvicorn running on")) { - console.log("Successfully started Continue python server"); + if (await checkServerRunning(serverUrl)) return; + + return new Promise(async (resolve, reject) => { + try { + const child = spawn(command, { + shell: true, + }); + child.stdout.on("data", (data: any) => { + console.log(`stdout: ${data}`); + }); + child.stderr.on("data", (data: any) => { + console.log(`stderr: ${data}`); + if (data.includes("Uvicorn running on")) { + console.log("Successfully started Continue python server"); + resolve(null); + } + }); + child.on("error", (error: any) => { + console.log(`error: ${error.message}`); + }); + } catch (e) { + console.log("Failed to start Continue python server", e); + // If failed, check if it's because the server is already running (might have happened just after we checked above) + if (await checkServerRunning(serverUrl)) { resolve(null); + } else { + reject(); } - }); - child.on("error", (error: any) => { - console.log(`error: ${error.message}`); - }); - } catch (e) { - console.log("Failed to start Continue python server", e); - // If failed, check if it's because the server is already running (might have happened just after we checked above) - if (await checkServerRunning(serverUrl)) { - resolve(null); - } else { - reject(); } - } + }); }); } diff --git a/extension/src/commands.ts b/extension/src/commands.ts index 424c238a..8072353b 100644 --- a/extension/src/commands.ts +++ b/extension/src/commands.ts @@ -3,7 +3,6 @@ import { decorationManager, showAnswerInTextEditor, showGutterSpinner, - writeAndShowUnitTest, } from "./decorations"; import { acceptSuggestionCommand, @@ -14,19 +13,8 @@ import { rejectAllSuggestionsCommand, } from "./suggestions"; import * as bridge from "./bridge"; -import { debugPanelWebview, setupDebugPanel } from "./debugPanel"; -// import { openCapturedTerminal } from "./terminal/terminalEmulator"; -import { getRightViewColumn } from "./util/vscode"; -import { - findSuspiciousCode, - runPythonScript, - writeUnitTestForFunction, -} from "./bridge"; +import { debugPanelWebview } from "./debugPanel"; import { sendTelemetryEvent, TelemetryEvent } from "./telemetry"; -import { getLanguageLibrary } from "./languages"; -import { SerializedDebugContext } from "./client"; -import { addFileSystemToDebugContext } from "./util/util"; -import { ideProtocolClient } from "./activation/activate"; // Copy everything over from extension.ts const commandsMap: { [command: string]: (...args: any) => any } = { @@ -71,72 +59,9 @@ const commandsMap: { [command: string]: (...args: any) => any } = { type: "focusContinueInput", }); }, - "continue.openCapturedTerminal": () => { - // Happens in webview resolution function - // openCapturedTerminal(); - }, - "continue.findSuspiciousCode": async ( - debugContext: SerializedDebugContext - ) => { - vscode.window.withProgress( - { - location: vscode.ProgressLocation.Notification, - title: "Finding suspicious code", - cancellable: false, - }, - async (progress, token) => { - let suspiciousCode = await findSuspiciousCode(debugContext); - debugContext.rangesInFiles = suspiciousCode; - let { filesystem } = addFileSystemToDebugContext(debugContext); - debugPanelWebview?.postMessage({ - type: "findSuspiciousCode", - codeLocations: suspiciousCode, - filesystem, - }); - } - ); - }, - "continue.debugTest": async (fileAndFunctionSpecifier: string) => { - sendTelemetryEvent(TelemetryEvent.AutoDebugThisTest); - let editor = vscode.window.activeTextEditor; - if (editor) editor.document.save(); - let { stdout } = await runPythonScript("run_unit_test.py", [ - fileAndFunctionSpecifier, - ]); - let traceback = getLanguageLibrary( - fileAndFunctionSpecifier.split("::")[0] - ).parseFirstStacktrace(stdout); - if (!traceback) { - vscode.window.showInformationMessage("The test passes!"); - return; - } - vscode.commands.executeCommand("continue.openContinueGUI").then(() => { - setTimeout(() => { - debugPanelWebview?.postMessage({ - type: "traceback", - value: traceback, - }); - }, 500); - }); - }, }; const textEditorCommandsMap: { [command: string]: (...args: any) => {} } = { - "continue.writeUnitTest": async (editor: vscode.TextEditor) => { - let position = editor.selection.active; - - let gutterSpinnerKey = showGutterSpinner(editor, position.line); - try { - let test = await writeUnitTestForFunction( - editor.document.fileName, - position - ); - writeAndShowUnitTest(editor.document.fileName, test); - } catch { - } finally { - decorationManager.deleteDecoration(gutterSpinnerKey); - } - }, "continue.writeDocstring": async (editor: vscode.TextEditor, _) => { sendTelemetryEvent(TelemetryEvent.GenerateDocstring); let gutterSpinnerKey = showGutterSpinner( @@ -203,20 +128,3 @@ async function answerQuestion( } ); } - -// async function suggestFixForAllWorkspaceProblems() { -// Something like this, just figure out the loops for diagnostics vs problems -// let problems = vscode.languages.getDiagnostics(); -// let codeSuggestions = await Promise.all(problems.map((problem) => { -// return bridge.suggestFixForProblem(problem[0].fsPath, problem[1]); -// })); -// for (const [uri, diagnostics] of problems) { -// for (let i = 0; i < diagnostics.length; i++) { -// let diagnostic = diagnostics[i]; -// let suggestedCode = codeSuggestions[i]; -// // If you're going to do this for a bunch of files at once, it will show the unsaved icon in the tab -// // BUT it would be better to have a single window to review all edits -// showSuggestion(uri.fsPath, diagnostic.range, suggestedCode) -// } -// } -// } diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index 877d9f3e..8ab3e075 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -15,7 +15,6 @@ import { debugPanelWebview, setupDebugPanel } from "./debugPanel"; import { FileEditWithFullContents } from "../schema/FileEditWithFullContents"; import fs = require("fs"); import { WebsocketMessenger } from "./util/messenger"; -import { CapturedTerminal } from "./terminal/terminalEmulator"; import { decorationManager } from "./decorations"; class IdeProtocolClient { @@ -334,17 +333,14 @@ class IdeProtocolClient { return rangeInFiles; } - public continueTerminal: CapturedTerminal | undefined; - async runCommand(command: string) { - if (!this.continueTerminal || this.continueTerminal.isClosed()) { - this.continueTerminal = new CapturedTerminal({ - name: "Continue", - }); + if (vscode.window.terminals.length) { + vscode.window.terminals[0].sendText(command); + } else { + const terminal = vscode.window.createTerminal(); + terminal.show(); + terminal.sendText(command); } - - this.continueTerminal.show(); - return await this.continueTerminal.runCommand(command); } sendCommandOutput(output: string) { diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts index bb98eb46..b0db590a 100644 --- a/extension/src/debugPanel.ts +++ b/extension/src/debugPanel.ts @@ -1,21 +1,11 @@ import * as vscode from "vscode"; -import { - debugApi, - getContinueServerUrl, - runPythonScript, - unittestApi, -} from "./bridge"; -import { writeAndShowUnitTest } from "./decorations"; -import { showSuggestion } from "./suggestions"; -import { getLanguageLibrary } from "./languages"; +import { getContinueServerUrl } from "./bridge"; import { getExtensionUri, getNonce, openEditorAndRevealRange, } from "./util/vscode"; -import { sendTelemetryEvent, TelemetryEvent } from "./telemetry"; -import { RangeInFile, SerializedDebugContext } from "./client"; -import { addFileSystemToDebugContext } from "./util/util"; +import { RangeInFile } from "./client"; const WebSocket = require("ws"); class StreamManager { @@ -146,6 +136,9 @@ export function setupDebugPanel( let extensionUri = getExtensionUri(); let scriptUri: string; let styleMainUri: string; + let vscMediaUrl: string = debugPanelWebview + .asWebviewUri(vscode.Uri.joinPath(extensionUri, "react-app/dist")) + .toString(); const isProduction = true; // context?.extensionMode === vscode.ExtensionMode.Development; if (!isProduction) { @@ -236,6 +229,7 @@ export function setupDebugPanel( vscMachineId: vscode.env.machineId, apiUrl: getContinueServerUrl(), sessionId, + vscMediaUrl, }); // // Listen for changes to server URL in settings @@ -273,71 +267,6 @@ export function setupDebugPanel( connection.send(data.message); break; } - case "listTenThings": { - sendTelemetryEvent(TelemetryEvent.GenerateIdeas); - let resp = await debugApi.listtenDebugListPost({ - serializedDebugContext: data.debugContext, - }); - panel.webview.postMessage({ - type: "listTenThings", - value: resp.completion, - }); - break; - } - case "suggestFix": { - let completion: string; - let codeSelection = data.debugContext.rangesInFiles?.at(0); - if (codeSelection) { - completion = ( - await debugApi.inlineDebugInlinePost({ - inlineBody: { - filecontents: await vscode.workspace.fs - .readFile(vscode.Uri.file(codeSelection.filepath)) - .toString(), - startline: codeSelection.range.start.line, - endline: codeSelection.range.end.line, - traceback: data.debugContext.traceback, - }, - }) - ).completion; - } else if (data.debugContext.traceback) { - completion = ( - await debugApi.suggestionDebugSuggestionGet({ - traceback: data.debugContext.traceback, - }) - ).completion; - } else { - break; - } - panel.webview.postMessage({ - type: "suggestFix", - value: completion, - }); - break; - } - case "findSuspiciousCode": { - let traceback = getLanguageLibrary(".py").parseFirstStacktrace( - data.debugContext.traceback - ); - if (traceback === undefined) return; - vscode.commands.executeCommand( - "continue.findSuspiciousCode", - data.debugContext - ); - break; - } - case "queryEmbeddings": { - let { results } = await runPythonScript("index.py query", [ - data.query, - 2, - vscode.workspace.workspaceFolders?.[0].uri.fsPath, - ]); - panel.webview.postMessage({ - type: "queryEmbeddings", - results, - }); - break; - } case "openFile": { openEditorAndRevealRange(data.path, undefined, vscode.ViewColumn.One); break; @@ -351,20 +280,6 @@ export function setupDebugPanel( streamManager.closeStream(); break; } - case "explainCode": { - sendTelemetryEvent(TelemetryEvent.ExplainCode); - let debugContext: SerializedDebugContext = addFileSystemToDebugContext( - data.debugContext - ); - let resp = await debugApi.explainDebugExplainPost({ - serializedDebugContext: debugContext, - }); - panel.webview.postMessage({ - type: "explainCode", - value: resp.completion, - }); - break; - } case "withProgress": { // This message allows withProgress to be used in the webview if (data.done) { @@ -395,83 +310,6 @@ export function setupDebugPanel( ); break; } - case "makeEdit": { - sendTelemetryEvent(TelemetryEvent.SuggestFix); - let suggestedEdits = data.edits; - - if ( - typeof suggestedEdits === "undefined" || - suggestedEdits.length === 0 - ) { - vscode.window.showInformationMessage( - "Continue couldn't find a fix for this error." - ); - return; - } - - for (let i = 0; i < suggestedEdits.length; i++) { - let edit = suggestedEdits[i]; - await showSuggestion( - edit.filepath, - new vscode.Range( - edit.range.start.line, - edit.range.start.character, - edit.range.end.line, - edit.range.end.character - ), - edit.replacement - ); - } - break; - } - case "generateUnitTest": { - sendTelemetryEvent(TelemetryEvent.CreateTest); - vscode.window.withProgress( - { - location: vscode.ProgressLocation.Notification, - title: "Generating Unit Test...", - cancellable: false, - }, - async () => { - for (let i = 0; i < data.debugContext.rangesInFiles?.length; i++) { - let codeSelection = data.debugContext.rangesInFiles?.at(i); - if ( - codeSelection && - codeSelection.filepath && - codeSelection.range - ) { - try { - let filecontents = ( - await vscode.workspace.fs.readFile( - vscode.Uri.file(codeSelection.filepath) - ) - ).toString(); - let resp = - await unittestApi.failingtestUnittestFailingtestPost({ - failingTestBody: { - fp: { - filecontents, - lineno: codeSelection.range.end.line, - }, - description: data.debugContext.description || "", - }, - }); - - if (resp.completion) { - let decorationKey = await writeAndShowUnitTest( - codeSelection.filepath, - resp.completion - ); - break; - } - } catch {} - } - } - } - ); - - break; - } } }); diff --git a/extension/src/decorations.ts b/extension/src/decorations.ts index d2c94135..0587110c 100644 --- a/extension/src/decorations.ts +++ b/extension/src/decorations.ts @@ -1,7 +1,5 @@ import * as vscode from "vscode"; -import { getRightViewColumn, getTestFile } from "./util/vscode"; import * as path from "path"; -import { getLanguageLibrary } from "./languages"; export function showAnswerInTextEditor( filename: string, @@ -223,98 +221,3 @@ export function highlightCode( return key; } - -// Show unit test -const pythonImportDistinguisher = (line: string): boolean => { - if (line.startsWith("from") || line.startsWith("import")) { - return true; - } - return false; -}; -const javascriptImportDistinguisher = (line: string): boolean => { - if (line.startsWith("import")) { - return true; - } - return false; -}; -const importDistinguishersMap: { - [fileExtension: string]: (line: string) => boolean; -} = { - js: javascriptImportDistinguisher, - ts: javascriptImportDistinguisher, - py: pythonImportDistinguisher, -}; -function getImportsFromFileString( - fileString: string, - importDistinguisher: (line: string) => boolean -): Set<string> { - let importLines = new Set<string>(); - for (let line of fileString.split("\n")) { - if (importDistinguisher(line)) { - importLines.add(line); - } - } - return importLines; -} -function removeRedundantLinesFrom( - fileContents: string, - linesToRemove: Set<string> -): string { - let fileLines = fileContents.split("\n"); - fileLines = fileLines.filter((line: string) => { - return !linesToRemove.has(line); - }); - return fileLines.join("\n"); -} - -export async function writeAndShowUnitTest( - filename: string, - test: string -): Promise<DecorationKey> { - return new Promise((resolve, reject) => { - let testFilename = getTestFile(filename, true); - vscode.workspace.openTextDocument(testFilename).then((doc) => { - let fileContent = doc.getText(); - let fileEmpty = fileContent.trim() === ""; - let existingImportLines = getImportsFromFileString( - fileContent, - importDistinguishersMap[doc.fileName.split(".").at(-1) || ".py"] - ); - - // Remove redundant imports, make sure pytest is there - test = removeRedundantLinesFrom(test, existingImportLines); - test = - (fileEmpty - ? `${getLanguageLibrary(".py").writeImport( - testFilename, - filename - )}\nimport pytest\n\n` - : "\n\n") + - test.trim() + - "\n"; - - vscode.window - .showTextDocument(doc, getRightViewColumn()) - .then((editor) => { - let lastLine = editor.document.lineAt(editor.document.lineCount - 1); - let testRange = new vscode.Range( - lastLine.range.end, - new vscode.Position( - test.split("\n").length + lastLine.range.end.line, - 0 - ) - ); - editor - .edit((edit) => { - edit.insert(lastLine.range.end, test); - return true; - }) - .then((success) => { - if (!success) reject("Failed to insert test"); - let key = highlightCode(editor, testRange); - resolve(key); - }); - }); - }); - }); -} diff --git a/extension/src/extension.ts b/extension/src/extension.ts index 88af0d19..de8f55e3 100644 --- a/extension/src/extension.ts +++ b/extension/src/extension.ts @@ -13,7 +13,7 @@ async function dynamicImportAndActivate( showTutorial: boolean ) { const { activateExtension } = await import("./activation/activate"); - activateExtension(context, showTutorial); + await activateExtension(context, showTutorial); } export function activate(context: vscode.ExtensionContext) { @@ -25,7 +25,6 @@ export function activate(context: vscode.ExtensionContext) { cancellable: false, }, async () => { - await startContinuePythonServer(); dynamicImportAndActivate(context, true); } ); diff --git a/extension/src/lang-server/codeLens.ts b/extension/src/lang-server/codeLens.ts index 629da0b8..1f352797 100644 --- a/extension/src/lang-server/codeLens.ts +++ b/extension/src/lang-server/codeLens.ts @@ -1,5 +1,4 @@ import * as vscode from "vscode"; -import { getLanguageLibrary } from "../languages"; import { editorToSuggestions } from "../suggestions"; class SuggestionsCodeLensProvider implements vscode.CodeLensProvider { @@ -56,37 +55,6 @@ class SuggestionsCodeLensProvider implements vscode.CodeLensProvider { } } -class PytestCodeLensProvider implements vscode.CodeLensProvider { - public provideCodeLenses( - document: vscode.TextDocument, - token: vscode.CancellationToken - ): vscode.CodeLens[] | Thenable<vscode.CodeLens[]> { - const codeLenses: vscode.CodeLens[] = []; - let lineno = 1; - const languageLibrary = getLanguageLibrary(document.fileName); - for (let line of document.getText().split("\n")) { - if ( - languageLibrary.lineIsFunctionDef(line) && - languageLibrary.parseFunctionDefForName(line).startsWith("test_") - ) { - const functionToTest = languageLibrary.parseFunctionDefForName(line); - const fileAndFunctionNameSpecifier = - document.fileName + "::" + functionToTest; - codeLenses.push( - new vscode.CodeLens(new vscode.Range(lineno, 0, lineno, 1), { - title: "Debug This Test", - command: "continue.debugTest", - arguments: [fileAndFunctionNameSpecifier], - }) - ); - } - lineno++; - } - - return codeLenses; - } -} - const allCodeLensProviders: { [langauge: string]: vscode.CodeLensProvider[] } = { // python: [new SuggestionsCodeLensProvider(), new PytestCodeLensProvider()], diff --git a/extension/src/languages/index.d.ts b/extension/src/languages/index.d.ts deleted file mode 100644 index be7ddfbc..00000000 --- a/extension/src/languages/index.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -export interface LanguageLibrary { - language: string; - fileExtensions: string[]; - parseFirstStacktrace: (stdout: string) => string | undefined; - lineIsFunctionDef: (line: string) => boolean; - parseFunctionDefForName: (line: string) => string; - lineIsComment: (line: string) => boolean; - writeImport: ( - sourcePath: string, - pathToImport: string, - namesToImport?: string[] | undefined - ) => string; -} diff --git a/extension/src/languages/index.ts b/extension/src/languages/index.ts deleted file mode 100644 index 31d73a0b..00000000 --- a/extension/src/languages/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -import pythonLanguageLibrary from "./python"; -import javascriptLanguageLibrary from "./javascript"; -import { LanguageLibrary } from "./index.d"; - -export const languageLibraries: LanguageLibrary[] = [ - pythonLanguageLibrary, - javascriptLanguageLibrary, -]; - -export function getLanguageLibrary(filepath: string): LanguageLibrary { - for (let languageLibrary of languageLibraries) { - for (let fileExtension of languageLibrary.fileExtensions) { - if (filepath.endsWith(fileExtension)) { - return languageLibrary; - } - } - } - throw new Error(`No language library found for file ${filepath}`); -} diff --git a/extension/src/languages/javascript/index.ts b/extension/src/languages/javascript/index.ts deleted file mode 100644 index 1c21a2fc..00000000 --- a/extension/src/languages/javascript/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { LanguageLibrary } from "../index.d"; -import { notImplemented } from "../notImplemented"; - -const NI = (propertyName: string) => notImplemented(propertyName, "javascript"); - -const javascriptLangaugeLibrary: LanguageLibrary = { - language: "javascript", - fileExtensions: [".js", ".jsx", ".ts", ".tsx"], - parseFirstStacktrace: NI("parseFirstStacktrace"), - lineIsFunctionDef: NI("lineIsFunctionDef"), - parseFunctionDefForName: NI("parseFunctionDefForName"), - lineIsComment: NI("lineIsComment"), - writeImport: NI("writeImport"), -}; - -export default javascriptLangaugeLibrary; diff --git a/extension/src/languages/notImplemented.ts b/extension/src/languages/notImplemented.ts deleted file mode 100644 index bbba2382..00000000 --- a/extension/src/languages/notImplemented.ts +++ /dev/null @@ -1,10 +0,0 @@ -export function notImplemented( - propertyName: string, - langauge: string -): (...args: any[]) => never { - return (...args: any[]) => { - throw new Error( - `Property ${propertyName} not implemented for language ${langauge}.` - ); - }; -} diff --git a/extension/src/languages/python/index.ts b/extension/src/languages/python/index.ts deleted file mode 100644 index 50282b45..00000000 --- a/extension/src/languages/python/index.ts +++ /dev/null @@ -1,74 +0,0 @@ -import path = require("path"); -import { LanguageLibrary } from "../index.d"; - -const tracebackStart = "Traceback (most recent call last):"; -const tracebackEnd = (buf: string): string | undefined => { - let lines = buf - .split("\n") - .filter((line: string) => line.trim() !== "~~^~~") - .filter((line: string) => line.trim() !== ""); - for (let i = 0; i < lines.length; i++) { - if ( - lines[i].startsWith(" File") && - i + 2 < lines.length && - lines[i + 2][0] !== " " - ) { - return lines.slice(0, i + 3).join("\n"); - } - } - return undefined; -}; - -function parseFirstStacktrace(stdout: string): string | undefined { - let startIdx = stdout.indexOf(tracebackStart); - if (startIdx < 0) return undefined; - stdout = stdout.substring(startIdx); - return tracebackEnd(stdout); -} - -function lineIsFunctionDef(line: string): boolean { - return line.startsWith("def "); -} - -function parseFunctionDefForName(line: string): string { - return line.split("def ")[1].split("(")[0]; -} - -function lineIsComment(line: string): boolean { - return line.trim().startsWith("#"); -} - -function writeImport( - sourcePath: string, - pathToImport: string, - namesToImport: string[] | undefined = undefined -): string { - let segs = path.relative(sourcePath, pathToImport).split(path.sep); - let importFrom = ""; - for (let seg of segs) { - if (seg === "..") { - importFrom = "." + importFrom; - } else { - if (!importFrom.endsWith(".")) { - importFrom += "."; - } - importFrom += seg.split(".").slice(0, -1).join("."); - } - } - - return `from ${importFrom} import ${ - namesToImport ? namesToImport.join(", ") : "*" - }`; -} - -const pythonLangaugeLibrary: LanguageLibrary = { - language: "python", - fileExtensions: [".py"], - parseFirstStacktrace, - lineIsFunctionDef, - parseFunctionDefForName, - lineIsComment, - writeImport, -}; - -export default pythonLangaugeLibrary; |