From efc1f3b9216d5bf503ecb58ff956f26a2bc4a130 Mon Sep 17 00:00:00 2001 From: Ben Force Date: Wed, 13 Mar 2024 07:05:49 -0500 Subject: [PATCH 1/6] feat: add gitlab context class --- .../GitLabMergeRequestContextProvider.ts | 185 ++++++++++++++++++ core/package-lock.json | 11 -- core/yarn.lock | 18 -- extensions/vscode/package-lock.json | 4 +- extensions/vscode/package.json | 2 +- extensions/vscode/yarn.lock | 23 +-- 6 files changed, 197 insertions(+), 46 deletions(-) create mode 100644 core/context/providers/GitLabMergeRequestContextProvider.ts diff --git a/core/context/providers/GitLabMergeRequestContextProvider.ts b/core/context/providers/GitLabMergeRequestContextProvider.ts new file mode 100644 index 0000000000..abdb0a1cdd --- /dev/null +++ b/core/context/providers/GitLabMergeRequestContextProvider.ts @@ -0,0 +1,185 @@ +import type { AxiosInstance, AxiosError } from "axios"; +import {BaseContextProvider} from ".."; +import { ContextProviderExtras, ContextItem, ContextProviderDescription } from "../.."; + +interface RemoteBranchInfo { + branch: string | null; + project: string | null; +} + +const trimFirstElement = (args: Array): string => { + return args[0].trim(); +}; + + +class GitLabMergeRequestContextProvider extends BaseContextProvider { + static description: ContextProviderDescription = { + title: 'gitlab-mr', + displayTitle: 'GitLab Merge Request', + description: 'Reference comments in a GitLab Merge Request', + type: 'normal' + }; + + private async getApi(): Promise { + const { default: Axios } = await import("axios"); + + const domain = this.options.domain ?? "gitlab.com"; + const token = this.options.token; + + if(!token) { + throw new Error(`GitLab Private Token is required!`); + } + + return Axios.create({ + baseURL: `https://${domain ?? "gitlab.com"}/api`, + headers: { + "PRIVATE-TOKEN": token, + }, + }); + }; + + + private async getRemoteBranchName(extras: ContextProviderExtras): Promise { + + const workingDir = await extras.ide + .getWorkspaceDirs() + .then(trimFirstElement); + + const subprocess = (command: string) => + extras.ide + .subprocess(`cd ${workingDir}; ${command}`) + .then(trimFirstElement); + + const branchName = await subprocess(`git branch --show-current`); + + const branchRemote = await subprocess( + `git config branch.${branchName}.remote` + ); + + const branchInfo = await subprocess(`git branch -vv`); + + const currentBranchInfo = branchInfo + .split("\n") + .find((line) => line.startsWith("*")); + + const remoteMatches = RegExp( + `\\[${branchRemote}/(?[^\\]]+)\\]` + ).exec(currentBranchInfo!); + + console.dir({ remoteMatches }); + + const remoteBranch = remoteMatches?.groups?.["remote_branch"] ?? null; + + const remoteUrl = await subprocess(`git remote get-url ${branchRemote}`); + + const urlMatches = RegExp(`:(?.*).git`).exec(remoteUrl); + + const project = urlMatches?.groups?.["project"] ?? null; + + return { + branch: remoteBranch, + project, + }; + }; + + async getContextItems(query: string, extras: ContextProviderExtras): Promise { + + const parts = [`# ${this.description.displayTitle}`]; + + const { branch, project } = await this.getRemoteBranchName(extras); + + const api = await this.getApi(); + + try { + const mergeRequests = await api + .get>( + `/v4/projects/${encodeURIComponent(project!)}/merge_requests`, + { + params: { + source_branch: branch, + state: "opened", + }, + } + ) + .then((x) => x.data); + + if (mergeRequests?.length) { + const mergeRequest = mergeRequests[0]; + + parts.push(`Merge Request: ${mergeRequest.iid}`); + + const comments = await api.get>( + `/v4/projects/${mergeRequest.project_id}/merge_requests/${mergeRequest.iid}/notes`, + { + params: { + sort: "asc", + order_by: "created_at", + }, + } + ); + + const locations = {} as Record>; + + for (const comment of comments.data.filter( + (x) => x.type === "DiffNote" + )) { + const filename = comment.position?.new_path ?? "general"; + + if (!locations[filename]) { + locations[filename] = []; + } + + locations[filename].push(comment); + } + + const commentFormatter = (comment: GitLabComment) => { + const commentParts = [ + `### ${comment.author.name} on ${comment.created_at}${ + comment.resolved ? " (Resolved)" : "" + }`, + ]; + + if (comment.position?.new_line) { + commentParts.push( + `line: ${comment.position.new_line}\ncommit: ${comment.position.head_sha}` + ); + } + + commentParts.push(comment.body); + + return commentParts.join("\n\n"); + }; + + for (const [filename, locationComments] of Object.entries(locations)) { + if (filename !== "general") { + parts.push(`## File ${filename}`); + locationComments.sort( + (a, b) => a.position!.new_line - b.position!.new_line + ); + } else { + parts.push("## Comments"); + } + + parts.push(...locationComments.map(commentFormatter)); + } + } + + const content = parts.join("\n\n"); + + return [ + { + name: `GitLab MR Comments`, + content, + description: `Comments from the Merge Request for this branch.`, + }, + ]; + } catch(ex) { + if(ex instanceof AxiosError) { + + } + } + } + +} + +export default GitLabMergeRequestContextProvider; \ No newline at end of file diff --git a/core/package-lock.json b/core/package-lock.json index ff37c9bcc6..d00ff5127b 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -55,7 +55,6 @@ "@types/uuid": "^9.0.7", "esbuild": "^0.19.11", "jest": "^29.7.0", - "tree-sitter-cli": "^0.21.0", "ts-jest": "^29.1.1" } }, @@ -9575,16 +9574,6 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "license": "MIT" }, - "node_modules/tree-sitter-cli": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.21.0.tgz", - "integrity": "sha512-wA7wT5724fNQW82XDH6zT6ZcYonjrAKLCHHuhLsPcAKULrhp3rNuMvlgBdB5FUBvmjHNhtTZF/qpHenMoRJPBw==", - "dev": true, - "hasInstallScript": true, - "bin": { - "tree-sitter": "cli.js" - } - }, "node_modules/tree-sitter-wasms": { "version": "0.1.6", "license": "Unlicense" diff --git a/core/yarn.lock b/core/yarn.lock index ee9b97f8ea..de8681ec2d 100644 --- a/core/yarn.lock +++ b/core/yarn.lock @@ -318,11 +318,6 @@ resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@esbuild/darwin-arm64@0.19.12": - version "0.19.12" - resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz" - integrity sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g== - "@eslint-community/eslint-utils@^4.2.0": version "4.4.0" dependencies: @@ -602,9 +597,6 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@lancedb/vectordb-darwin-arm64@0.4.11": - version "0.4.11" - "@mozilla/readability@^0.5.0": version "0.5.0" resolved "https://registry.npmjs.org/@mozilla/readability/-/readability-0.5.0.tgz" @@ -2501,11 +2493,6 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.2: - version "2.3.3" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - function-bind@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" @@ -5264,11 +5251,6 @@ tr46@~0.0.3: resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== -tree-sitter-cli@^0.21.0: - version "0.21.0" - resolved "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.21.0.tgz" - integrity sha512-wA7wT5724fNQW82XDH6zT6ZcYonjrAKLCHHuhLsPcAKULrhp3rNuMvlgBdB5FUBvmjHNhtTZF/qpHenMoRJPBw== - tree-sitter-wasms@^0.1.6: version "0.1.6" diff --git a/extensions/vscode/package-lock.json b/extensions/vscode/package-lock.json index 9859539f91..f0c69c6e52 100644 --- a/extensions/vscode/package-lock.json +++ b/extensions/vscode/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.9.84", + "version": "0.9.85", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "continue", - "version": "0.9.84", + "version": "0.9.85", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extensions/vscode/package.json b/extensions/vscode/package.json index b932feff04..5c7a840180 100644 --- a/extensions/vscode/package.json +++ b/extensions/vscode/package.json @@ -1,7 +1,7 @@ { "name": "continue", "icon": "media/icon.png", - "version": "0.9.84", + "version": "0.9.85", "repository": { "type": "git", "url": "https://github.com/continuedev/continue" diff --git a/extensions/vscode/yarn.lock b/extensions/vscode/yarn.lock index 58e092a54d..66932aca88 100644 --- a/extensions/vscode/yarn.lock +++ b/extensions/vscode/yarn.lock @@ -368,15 +368,15 @@ tar "^6.0.5" yargs "^17.0.1" -"@esbuild/darwin-arm64@0.17.19": +"@esbuild/linux-x64@0.17.19": version "0.17.19" - resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz" - integrity sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg== + resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz" + integrity sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw== -"@esbuild/darwin-arm64@0.18.20": +"@esbuild/linux-x64@0.18.20": version "0.18.20" - resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz" - integrity sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA== + resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz" + integrity sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w== "@eslint-community/eslint-utils@^4.2.0": version "4.4.0" @@ -691,10 +691,10 @@ resolved "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz" integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== -"@lancedb/vectordb-darwin-arm64@*", "@lancedb/vectordb-darwin-arm64@0.4.12": +"@lancedb/vectordb-linux-x64-gnu@0.4.12": version "0.4.12" - resolved "https://registry.npmjs.org/@lancedb/vectordb-darwin-arm64/-/vectordb-darwin-arm64-0.4.12.tgz" - integrity sha512-38/rkJRlWXkPWXuj9onzvbrhnIWcIUQjgEp5G9v5ixPosBowm7A4j8e2Q8CJMsVSNcVX2JLqwWVldiWegZFuYw== + resolved "https://registry.npmjs.org/@lancedb/vectordb-linux-x64-gnu/-/vectordb-linux-x64-gnu-0.4.12.tgz" + integrity sha512-gJqYR0aymrS+C60xc4EQPzmQ5/69XfeFv2ofBvAj7qW+c6BcnoAcfVl+7s1IrcWeGz251sm5cD5Lx4AzJd89dA== "@lukeed/csprng@^1.0.0": version "1.1.0" @@ -3444,11 +3444,6 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.2, fsevents@~2.3.2: - version "2.3.3" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - function-bind@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" From 0ace1699356be8024a8ab49ccadc4b1b22c18e08 Mon Sep 17 00:00:00 2001 From: Ben Force Date: Wed, 13 Mar 2024 07:34:22 -0500 Subject: [PATCH 2/6] feat: update to work as normal context provider --- .../GitLabMergeRequestContextProvider.ts | 103 ++++++++++++++---- 1 file changed, 82 insertions(+), 21 deletions(-) diff --git a/core/context/providers/GitLabMergeRequestContextProvider.ts b/core/context/providers/GitLabMergeRequestContextProvider.ts index abdb0a1cdd..f78cd10a1e 100644 --- a/core/context/providers/GitLabMergeRequestContextProvider.ts +++ b/core/context/providers/GitLabMergeRequestContextProvider.ts @@ -1,4 +1,4 @@ -import type { AxiosInstance, AxiosError } from "axios"; +import { AxiosInstance, AxiosError } from "axios"; import {BaseContextProvider} from ".."; import { ContextProviderExtras, ContextItem, ContextProviderDescription } from "../.."; @@ -7,6 +7,51 @@ interface RemoteBranchInfo { project: string | null; } +interface GitLabUser { + id: number; + username: string; + name: string; + state: "active"; + locked: boolean; + avatar_url: string; + web_url: string; +} + +interface GitLabMergeRequest { + iid: number; + project_id: number; + title: string; + description: string; +} + +interface GitLabComment { + type: null | "DiffNote"; + resolvable: boolean; + resolved?: boolean; + body: string; + created_at: string; + author: GitLabUser; + position?: { + new_path: string; + new_line: number; + head_sha: string; + line_range: { + start: { + line_code: string; + type: "new"; + old_line: null; + new_line: number; + }; + end: { + line_code: string; + type: "new"; + old_line: null; + new_line: number; + }; + }; + }; +} + const trimFirstElement = (args: Array): string => { return args[0].trim(); }; @@ -31,7 +76,7 @@ class GitLabMergeRequestContextProvider extends BaseContextProvider { } return Axios.create({ - baseURL: `https://${domain ?? "gitlab.com"}/api`, + baseURL: `https://${domain ?? "gitlab.com"}/api/v4`, headers: { "PRIVATE-TOKEN": token, }, @@ -82,18 +127,18 @@ class GitLabMergeRequestContextProvider extends BaseContextProvider { }; }; - async getContextItems(query: string, extras: ContextProviderExtras): Promise { - - const parts = [`# ${this.description.displayTitle}`]; + async getContextItems(query: string, extras: ContextProviderExtras): Promise { const { branch, project } = await this.getRemoteBranchName(extras); const api = await this.getApi(); + + const result = [] as Array; try { const mergeRequests = await api - .get>( - `/v4/projects/${encodeURIComponent(project!)}/merge_requests`, + .get>( + `/projects/${encodeURIComponent(project!)}/merge_requests`, { params: { source_branch: branch, @@ -103,13 +148,17 @@ class GitLabMergeRequestContextProvider extends BaseContextProvider { ) .then((x) => x.data); - if (mergeRequests?.length) { - const mergeRequest = mergeRequests[0]; - - parts.push(`Merge Request: ${mergeRequest.iid}`); + for (const mergeRequest of mergeRequests) { + const parts = [ + `# ${mergeRequest.title}` + ]; + + if (mergeRequest?.description) { + parts.push(`## Description`, mergeRequest.description); + } const comments = await api.get>( - `/v4/projects/${mergeRequest.project_id}/merge_requests/${mergeRequest.iid}/notes`, + `/projects/${mergeRequest.project_id}/merge_requests/${mergeRequest.iid}/notes`, { params: { sort: "asc", @@ -117,12 +166,14 @@ class GitLabMergeRequestContextProvider extends BaseContextProvider { }, } ); + + const filteredComments = comments.data.filter( + (x) => x.type === "DiffNote" + ); const locations = {} as Record>; - for (const comment of comments.data.filter( - (x) => x.type === "DiffNote" - )) { + for (const comment of filteredComments) { const filename = comment.position?.new_path ?? "general"; if (!locations[filename]) { @@ -162,22 +213,32 @@ class GitLabMergeRequestContextProvider extends BaseContextProvider { parts.push(...locationComments.map(commentFormatter)); } - } - + + const content = parts.join("\n\n"); - return [ + result.push( { - name: `GitLab MR Comments`, + name: mergeRequest.title, content, description: `Comments from the Merge Request for this branch.`, }, - ]; + ); + } + } catch(ex) { if(ex instanceof AxiosError) { - + if (ex.response) { + throw ex.response?.data ?? new Error(`GitLab error ${ex.response.status}: ${ex.response.statusText}`); + } else { + throw new Error(`GitLab Request Error ${ex.request}`); + } } + + throw ex; } + + return result; } } From ae888d9e814cf10931222070ed1e2ae5438325fe Mon Sep 17 00:00:00 2001 From: Ben Force Date: Fri, 15 Mar 2024 05:48:20 -0500 Subject: [PATCH 3/6] fix: add context provider --- core/context/providers/index.ts | 2 ++ core/index.d.ts | 3 +- docs/static/schemas/config.json | 27 +++++++++++++++ .../src/main/resources/config_schema.json | 27 +++++++++++++++ extensions/vscode/config_schema.json | 27 +++++++++++++++ extensions/vscode/continue_rc_schema.json | 33 +++++++++++++++++++ 6 files changed, 118 insertions(+), 1 deletion(-) diff --git a/core/context/providers/index.ts b/core/context/providers/index.ts index 978c8e94c3..58016c3b73 100644 --- a/core/context/providers/index.ts +++ b/core/context/providers/index.ts @@ -17,6 +17,7 @@ import ProblemsContextProvider from "./ProblemsContextProvider"; import SearchContextProvider from "./SearchContextProvider"; import TerminalContextProvider from "./TerminalContextProvider"; import URLContextProvider from "./URLContextProvider"; +import GitLabMergeRequestContextProvider from "./GitLabMergeRequestContextProvider"; const Providers: (typeof BaseContextProvider)[] = [ DiffContextProvider, @@ -32,6 +33,7 @@ const Providers: (typeof BaseContextProvider)[] = [ ProblemsContextProvider, FolderContextProvider, DocsContextProvider, + GitLabMergeRequestContextProvider, // CodeHighlightsContextProvider, // CodeOutlineContextProvider, JiraIssuesContextProvider, diff --git a/core/index.d.ts b/core/index.d.ts index 18f88a2cd3..c42bc662b2 100644 --- a/core/index.d.ts +++ b/core/index.d.ts @@ -428,7 +428,8 @@ type ContextProviderName = | "postgres" | "database" | "code" - | "docs"; + | "docs" + | "gitlab-mr"; type TemplateType = | "llama2" diff --git a/docs/static/schemas/config.json b/docs/static/schemas/config.json index 7734d7cefb..439e6c0d59 100644 --- a/docs/static/schemas/config.json +++ b/docs/static/schemas/config.json @@ -1241,6 +1241,33 @@ "required": ["connections"] } }, + { + "if": { + "properties": { + "name": { + "enum": ["gitlab-mr"] + } + } + }, + "then": { + "properties": { + "params": { + "properties": { + "domain": { + "type": "string", + "description": "Your GitLab domain, will default to gitlab.com" + }, + "token": { + "type": "string", + "description": "Your private access token." + } + }, + "required": ["token"] + } + }, + "required": ["params"] + } + }, { "if": { "properties": { diff --git a/extensions/intellij/src/main/resources/config_schema.json b/extensions/intellij/src/main/resources/config_schema.json index 7734d7cefb..439e6c0d59 100644 --- a/extensions/intellij/src/main/resources/config_schema.json +++ b/extensions/intellij/src/main/resources/config_schema.json @@ -1241,6 +1241,33 @@ "required": ["connections"] } }, + { + "if": { + "properties": { + "name": { + "enum": ["gitlab-mr"] + } + } + }, + "then": { + "properties": { + "params": { + "properties": { + "domain": { + "type": "string", + "description": "Your GitLab domain, will default to gitlab.com" + }, + "token": { + "type": "string", + "description": "Your private access token." + } + }, + "required": ["token"] + } + }, + "required": ["params"] + } + }, { "if": { "properties": { diff --git a/extensions/vscode/config_schema.json b/extensions/vscode/config_schema.json index 7734d7cefb..439e6c0d59 100644 --- a/extensions/vscode/config_schema.json +++ b/extensions/vscode/config_schema.json @@ -1241,6 +1241,33 @@ "required": ["connections"] } }, + { + "if": { + "properties": { + "name": { + "enum": ["gitlab-mr"] + } + } + }, + "then": { + "properties": { + "params": { + "properties": { + "domain": { + "type": "string", + "description": "Your GitLab domain, will default to gitlab.com" + }, + "token": { + "type": "string", + "description": "Your private access token." + } + }, + "required": ["token"] + } + }, + "required": ["params"] + } + }, { "if": { "properties": { diff --git a/extensions/vscode/continue_rc_schema.json b/extensions/vscode/continue_rc_schema.json index 9960451534..7fdd0279b4 100644 --- a/extensions/vscode/continue_rc_schema.json +++ b/extensions/vscode/continue_rc_schema.json @@ -1390,6 +1390,39 @@ ] } }, + { + "if": { + "properties": { + "name": { + "enum": [ + "gitlab-mr" + ] + } + } + }, + "then": { + "properties": { + "params": { + "properties": { + "domain": { + "type": "string", + "description": "Your GitLab domain, will default to gitlab.com" + }, + "token": { + "type": "string", + "description": "Your private access token." + } + }, + "required": [ + "token" + ] + } + }, + "required": [ + "params" + ] + } + }, { "if": { "properties": { From e3653f98f08e52fa6a55d41ca147add1c8e5515a Mon Sep 17 00:00:00 2001 From: Ben Force Date: Fri, 15 Mar 2024 07:07:26 -0500 Subject: [PATCH 4/6] feat: add code that comment is based on --- .../GitLabMergeRequestContextProvider.ts | 88 ++++++++++++------- 1 file changed, 56 insertions(+), 32 deletions(-) diff --git a/core/context/providers/GitLabMergeRequestContextProvider.ts b/core/context/providers/GitLabMergeRequestContextProvider.ts index f78cd10a1e..c845c959c5 100644 --- a/core/context/providers/GitLabMergeRequestContextProvider.ts +++ b/core/context/providers/GitLabMergeRequestContextProvider.ts @@ -56,6 +56,17 @@ const trimFirstElement = (args: Array): string => { return args[0].trim(); }; +const getSubprocess = async (extras: ContextProviderExtras) => { + const workingDir = await extras.ide + .getWorkspaceDirs() + .then(trimFirstElement); + + return (command: string) => + extras.ide + .subprocess(`cd ${workingDir}; ${command}`) + .then(trimFirstElement); +}; + class GitLabMergeRequestContextProvider extends BaseContextProvider { static description: ContextProviderDescription = { @@ -86,14 +97,7 @@ class GitLabMergeRequestContextProvider extends BaseContextProvider { private async getRemoteBranchName(extras: ContextProviderExtras): Promise { - const workingDir = await extras.ide - .getWorkspaceDirs() - .then(trimFirstElement); - - const subprocess = (command: string) => - extras.ide - .subprocess(`cd ${workingDir}; ${command}`) - .then(trimFirstElement); + const subprocess = await getSubprocess(extras); const branchName = await subprocess(`git branch --show-current`); @@ -147,15 +151,15 @@ class GitLabMergeRequestContextProvider extends BaseContextProvider { } ) .then((x) => x.data); + + + const subprocess = await getSubprocess(extras); for (const mergeRequest of mergeRequests) { const parts = [ - `# ${mergeRequest.title}` + `# GitLab Merge Request\ntitle: "${mergeRequest.title}"\ndescription: "${mergeRequest.description ?? 'None'}"`, + `## Comments`, ]; - - if (mergeRequest?.description) { - parts.push(`## Description`, mergeRequest.description); - } const comments = await api.get>( `/projects/${mergeRequest.project_id}/merge_requests/${mergeRequest.iid}/notes`, @@ -183,35 +187,43 @@ class GitLabMergeRequestContextProvider extends BaseContextProvider { locations[filename].push(comment); } - const commentFormatter = (comment: GitLabComment) => { - const commentParts = [ - `### ${comment.author.name} on ${comment.created_at}${ - comment.resolved ? " (Resolved)" : "" - }`, - ]; + const commentFormatter = async (comment: GitLabComment) => { + const commentLabel = comment.body.includes("```suggestion") ? 'Code Suggestion' : 'Comment'; + let result = `#### ${commentLabel}\nauthor: "${comment.author.name}"\ndate: "${comment.created_at}"\nresolved: ${ + comment.resolved ? "Yes" : "No" + }`; if (comment.position?.new_line) { - commentParts.push( - `line: ${comment.position.new_line}\ncommit: ${comment.position.head_sha}` - ); + result += `\nline: ${comment.position.new_line}`; + + if (comment.position.head_sha) { + const sourceLines = await subprocess(`git show ${comment.position.head_sha}:${comment.position.new_path}`).then(result => result.split("\n")).catch(ex => []); + + const line = comment.position.new_line <= sourceLines.length ? sourceLines[comment.position.new_line - 1] : null; + + if (line) { + result += `\nsource: \`${line}\``; + } + } } - commentParts.push(comment.body); + result += `\n\n${comment.body}`; - return commentParts.join("\n\n"); + return result; }; for (const [filename, locationComments] of Object.entries(locations)) { if (filename !== "general") { - parts.push(`## File ${filename}`); + parts.push(`### File ${filename}`); locationComments.sort( (a, b) => a.position!.new_line - b.position!.new_line ); } else { - parts.push("## Comments"); + parts.push("### General"); } - parts.push(...locationComments.map(commentFormatter)); + const commentSections = await Promise.all(locationComments.map(commentFormatter)); + parts.push(...commentSections); } @@ -226,16 +238,28 @@ class GitLabMergeRequestContextProvider extends BaseContextProvider { ); } - } catch(ex) { - if(ex instanceof AxiosError) { + } catch (ex) { + let content = `# GitLab Merge Request\n\nError getting merge request. `; + if (ex instanceof AxiosError) { if (ex.response) { - throw ex.response?.data ?? new Error(`GitLab error ${ex.response.status}: ${ex.response.statusText}`); + const errorMessage = ex.response?.data ? ex.response.data.message ?? JSON.stringify(ex.response?.data) : `${ex.response.status}: ${ex.response.statusText}`; + content += `GitLab Error: ${errorMessage}`; } else { - throw new Error(`GitLab Request Error ${ex.request}`); + content += `GitLab Request Error ${ex.request}`; } + } else { + // @ts-ignore + content += `Unknown error: ${ex.message ?? JSON.stringify(ex)}`; } - throw ex; + + result.push( + { + name: `GitLab Merge Request`, + content, + description: `Error getting the Merge Request for this branch.`, + }, + ); } return result; From f0ad750d6cbdbfdb6ff57c5ce371134569956757 Mon Sep 17 00:00:00 2001 From: Ben Force Date: Fri, 15 Mar 2024 07:14:02 -0500 Subject: [PATCH 5/6] docs: add documentation --- docs/docs/customization/context-providers.md | 31 ++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/docs/customization/context-providers.md b/docs/docs/customization/context-providers.md index 3328f2fbbb..d17835a3df 100644 --- a/docs/docs/customization/context-providers.md +++ b/docs/docs/customization/context-providers.md @@ -120,6 +120,37 @@ Type '@issue' to reference the conversation in a GitHub issue. Make sure to incl } ``` +### GitLab Merge Request + +Type `@gitlab-mr` to reference an open MR for this branch on GitLab. + +#### Configuration + +You will need to create a [personal access token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html) with the `read_api` scope. then add the following to your configuration: + +```json +{ + "name": "gitlab-mr", + "params": { + "token": "..." + } +} +``` + +#### Using Self-Hosted GitLab + +You can specify the domain to communicate with by setting the `domain` parameter in your configurtion. By default this is set to `gitlab.com`. + +```json +{ + "name": "gitlab-mr", + "params": { + "token": "...", + "domain": "gitlab.example.com" + } +} +``` + ### Jira Issues Type '@jira' to reference the conversation in a Jira issue. Make sure to include your own [Atlassian API Token](https://id.atlassian.com/manage-profile/security/api-tokens). From 471ca9eae5e1f82e04b2d6d600631bf24876ec06 Mon Sep 17 00:00:00 2001 From: Ben Force Date: Fri, 15 Mar 2024 12:43:33 -0500 Subject: [PATCH 6/6] feat: enable comment filtering --- .../context/providers/GitLabMergeRequestContextProvider.ts | 7 +++++++ docs/docs/customization/context-providers.md | 4 ++++ docs/static/schemas/config.json | 4 ++++ extensions/intellij/src/main/resources/config_schema.json | 4 ++++ extensions/vscode/config_schema.json | 4 ++++ extensions/vscode/continue_rc_schema.json | 4 ++++ 6 files changed, 27 insertions(+) diff --git a/core/context/providers/GitLabMergeRequestContextProvider.ts b/core/context/providers/GitLabMergeRequestContextProvider.ts index c845c959c5..d1b3da8ebe 100644 --- a/core/context/providers/GitLabMergeRequestContextProvider.ts +++ b/core/context/providers/GitLabMergeRequestContextProvider.ts @@ -186,6 +186,13 @@ class GitLabMergeRequestContextProvider extends BaseContextProvider { locations[filename].push(comment); } + + if (extras.selectedCode.length && this.options.filterComments) { + const toRemove = Object.keys(locations).filter(filename => !extras.selectedCode.find(selection => selection.filepath.endsWith(filename)) && filename !== "general"); + for (const filepath of toRemove) { + delete locations[filepath]; + } + } const commentFormatter = async (comment: GitLabComment) => { const commentLabel = comment.body.includes("```suggestion") ? 'Code Suggestion' : 'Comment'; diff --git a/docs/docs/customization/context-providers.md b/docs/docs/customization/context-providers.md index d17835a3df..94cbcb2edc 100644 --- a/docs/docs/customization/context-providers.md +++ b/docs/docs/customization/context-providers.md @@ -151,6 +151,10 @@ You can specify the domain to communicate with by setting the `domain` parameter } ``` +#### Filtering Comments + +If you select some code to be edited, you can have the context provider filter out comments for other files. To enable this feature, set `filterComments` to `true`. + ### Jira Issues Type '@jira' to reference the conversation in a Jira issue. Make sure to include your own [Atlassian API Token](https://id.atlassian.com/manage-profile/security/api-tokens). diff --git a/docs/static/schemas/config.json b/docs/static/schemas/config.json index 439e6c0d59..4d9ff78fd6 100644 --- a/docs/static/schemas/config.json +++ b/docs/static/schemas/config.json @@ -1260,6 +1260,10 @@ "token": { "type": "string", "description": "Your private access token." + }, + "filterComments": { + "type": "boolean", + "description": "If you have code selected, filters out comments that aren't related to the selection." } }, "required": ["token"] diff --git a/extensions/intellij/src/main/resources/config_schema.json b/extensions/intellij/src/main/resources/config_schema.json index 439e6c0d59..4d9ff78fd6 100644 --- a/extensions/intellij/src/main/resources/config_schema.json +++ b/extensions/intellij/src/main/resources/config_schema.json @@ -1260,6 +1260,10 @@ "token": { "type": "string", "description": "Your private access token." + }, + "filterComments": { + "type": "boolean", + "description": "If you have code selected, filters out comments that aren't related to the selection." } }, "required": ["token"] diff --git a/extensions/vscode/config_schema.json b/extensions/vscode/config_schema.json index 439e6c0d59..4d9ff78fd6 100644 --- a/extensions/vscode/config_schema.json +++ b/extensions/vscode/config_schema.json @@ -1260,6 +1260,10 @@ "token": { "type": "string", "description": "Your private access token." + }, + "filterComments": { + "type": "boolean", + "description": "If you have code selected, filters out comments that aren't related to the selection." } }, "required": ["token"] diff --git a/extensions/vscode/continue_rc_schema.json b/extensions/vscode/continue_rc_schema.json index 7fdd0279b4..277388a9ec 100644 --- a/extensions/vscode/continue_rc_schema.json +++ b/extensions/vscode/continue_rc_schema.json @@ -1411,6 +1411,10 @@ "token": { "type": "string", "description": "Your private access token." + }, + "filterComments": { + "type": "boolean", + "description": "If you have code selected, filters out comments that aren't related to the selection." } }, "required": [