Skip to content

Commit

Permalink
push to git
Browse files Browse the repository at this point in the history
  • Loading branch information
hieunc229 committed Jun 30, 2021
0 parents commit 5ec0d24
Show file tree
Hide file tree
Showing 13 changed files with 17,037 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
vscode.d.ts
vscode.proposed.d.ts
20 changes: 20 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**@type {import('eslint').Linter.Config} */
// eslint-disable-next-line no-undef
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
plugins: [
'@typescript-eslint',
],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
],
rules: {
'semi': [2, "always"],
'@typescript-eslint/no-unused-vars': 0,
'@typescript-eslint/no-explicit-any': 0,
'@typescript-eslint/explicit-module-boundary-types': 0,
'@typescript-eslint/no-non-null-assertion': 0,
}
};
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
out
node_modules
.vscode-test/
*.vsix

yarn.lock
package-lock.json
21 changes: 21 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// A launch configuration that compiles the extension and then opens it inside a new window
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"configurations": [{
"name": "Run Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/out/**/*.js"
],
"preLaunchTask": "npm: watch"
}
]
}
20 changes: 20 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "watch",
"problemMatcher": "$tsc-watch",
"isBackground": true,
"presentation": {
"reveal": "never"
},
"group": {
"kind": "build",
"isDefault": true
}
}
]
}
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copilot clone

A clone version of [Github Copilot](https://copilot.github.com/). Instead of using AI, this extension send your search query to google, retrive stackoverflow answers and autocomplete them for you.

![Demo Video](./demo.gif)

Note: vscode inline-completions feature is still not ready for production (VSCode store won't accept extension like this yet). Check out the original [Microsoft/examples/inline-completions](https://github.com/microsoft/vscode-extension-samples/tree/main/inline-completions) repository


## Running the Sample

- Download this repository to your local machine
- Run `npm install` in terminal to install dependencies. _A `postinstall` script would download latest version of `vscode.proposed.d.ts`_
- Run the `Run Extension` target in the Debug View. Or from the top menu, choose `Run > Start Debugging`.

This will:
- Start a task `npm: watch` to compile the code
- Run the extension in a new VS Code window (you should play the code here)

## Play with Copilot

To trigger inline completion, you'll need to type `//find {your keyword}.` (start with `//find`, end with a dot `.`)

For example
```js
//find binary search in javascript.
```

Make sure that `showInlineCompletions` is enabled in your settings!
```
"editor.inlineSuggest.enabled": true
```

# Have fun
Binary file added demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
57 changes: 57 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"enableProposedApi": true,
"name": "inline-completion-sample",
"displayName": "Inline Completion Sample",
"description": "Sample showing how to implement an inline completion provider",
"version": "0.0.1",
"publisher": "vscode-samples",
"repository": "https://github.com/Microsoft/vscode-extension-samples",
"engines": {
"vscode": "^1.34.0"
},
"categories": [
"Other"
],
"activationEvents": [
"*"
],
"main": "./out/extension.js",
"contributes": {
"commands": [
{
"command": "extension.inline-completion-settings",
"title": "Inline Completion Settings"
}
],
"menus": {
"editor/inlineCompletions/actions": [
{
"command": "extension.inline-completion-settings"
}
]
}
},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"lint": "eslint . --ext .ts,.tsx",
"watch": "tsc -watch -p ./",
"download-api": "vscode-dts dev",
"postdownload-api": "vscode-dts main",
"postinstall": "npm run download-api"
},
"devDependencies": {
"@types/node": "^12.12.0",
"@types/node-fetch": "^2.5.10",
"@typescript-eslint/eslint-plugin": "^4.16.0",
"@typescript-eslint/parser": "^4.16.0",
"eslint": "^7.21.0",
"typescript": "^4.2.2",
"vscode-dts": "^0.3.1"
},
"dependencies": {
"@types/jsdom": "^16.2.12",
"jsdom": "^16.6.0",
"node-fetch": "^2.6.1"
}
}
76 changes: 76 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import * as vscode from 'vscode';
import { search } from './search';

const KEYWORD = `//find`

export function activate(context: vscode.ExtensionContext) {
const disposable = vscode.commands.registerCommand(
'extension.inline-completion-settings',
() => {
vscode.window.showInformationMessage('Show settings');
}
);

context.subscriptions.push(disposable);

function longestSuffixPrefixLength(a: string, b: string): number {
for (let i = Math.min(a.length, b.length); i > 0; i--) {
if (a.substr(-i) == b.substr(0, i)) {
return i;
}
}
return 0;
}

interface CustomInlineCompletionItem extends vscode.InlineCompletionItem {
trackingId: string;
}

const provider: vscode.InlineCompletionItemProvider<CustomInlineCompletionItem> = {
provideInlineCompletionItems: async (document, position, context, token) => {
const textBeforeCursor = document.getText(
new vscode.Range(position.with(undefined, 0), position)
);

if (textBeforeCursor.indexOf(KEYWORD) == 0 && textBeforeCursor[textBeforeCursor.length - 1] === ".") {

const rs = await search(textBeforeCursor)

if (rs == null) {
return { items: [] }
}

const suggestions = rs.results.map(item => item.code)
const items = new Array<CustomInlineCompletionItem>();

suggestions.forEach(code => {
const l = longestSuffixPrefixLength(textBeforeCursor, code);
items.push({
text: `\n` + code,
range: new vscode.Range(position.translate(0, -l), position),
trackingId: 'some-id',
});
})
// for (const s of suggestions) {
// const l = longestSuffixPrefixLength(textBeforeCursor, s);
// if (l > 0) {
// items.push({
// text: s,
// range: new vscode.Range(position.translate(0, -l), position),
// trackingId: 'some-id',
// });
// }
// }
return { items };
}
return { items: [] }
},
};

vscode.languages.registerInlineCompletionItemProvider({ pattern: "**" }, provider);

// Be aware that the API around `getInlineCompletionItemController` will not be finalized as is!
vscode.window.getInlineCompletionItemController(provider).onDidShowCompletionItem(e => {
const id = e.completionItem.trackingId;
});
}
65 changes: 65 additions & 0 deletions src/search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import fetch from "node-fetch";
import { JSDOM } from "jsdom";

const GURL = `https://www.google.com/search?q=site%3Astackoverflow.com+`;

type SnippetResult = {
results: { votes: number, code:string, textContent: string }[],
url: string
}

// Get search results from google, then return a list of stackoverflow links
function getGoogleResults(keyword: string): Promise<string[] | null> {
return new Promise((resolve, reject) => {
return fetch(`${GURL}${keyword.replace(/\s/, '+')}`)
.then(rs => rs.text())
.then(rs => resolve(
rs.match(/(https:\/\/stackoverflow.com\/[a-z0-9-/]+)/g))
)
.catch(reject)
})

}

async function getStackoverflowContent(url: string): Promise<{ content: string, url: string }> {
return new Promise((resolve, reject) => {
return fetch(url)
.then(rs => rs.text())
.then(rs => resolve({ content: rs, url }))
.catch(reject)
})
}

// Extract and sort stackoverflow answers
function getSnippetResults(options: { content: string, url: string }): SnippetResult {
var doc = new JSDOM(options.content)

let els = Array.from(doc.window.document.querySelectorAll(".answer"))
.filter((item: any) => item.querySelector("code") != null)

let results = els.map((item: any) => ({
textContent: item.textContent,
votes: parseInt(item.querySelector(".js-vote-count").textContent),
code: item.querySelector("code").textContent
}))

// Sort results by code length
results.sort((a,b) => b.code.length - a.code.length)

return { url: options.url, results }
}

// Send search query to google, get answers from stackoverflow
// then extract and return code results
export async function search(keyword: string) {
const urls = await getGoogleResults(keyword)

if (urls === null) {
return null
}

// Iterate through all urls if you want
// I also use the first results to avoid having too many requests
const snippets = await getStackoverflowContent(urls[0]);
return getSnippetResults(snippets)
}
14 changes: 14 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2019",
"lib": ["ES2019"],
"outDir": "out",
"sourceMap": true,
"strict": true,
"rootDir": "src",
"skipDefaultLibCheck": true,
"skipLibCheck": true
},
"exclude": ["node_modules", ".vscode-test"]
}
Loading

0 comments on commit 5ec0d24

Please sign in to comment.