Skip to content

Commit 3b31750

Browse files
committed
vscode extension
1 parent 34d27a3 commit 3b31750

File tree

11 files changed

+301
-12
lines changed

11 files changed

+301
-12
lines changed

python_autocomplete/evaluate.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import string
2-
from typing import List, Dict
2+
from typing import List, Dict, Set, Optional
33

44
import torch
55
import torch.nn
@@ -56,12 +56,13 @@ def get_next_char(self, prompt: str) -> str:
5656
best = prediction.argmax(-1).squeeze().item()
5757
return self.itos[best]
5858

59-
def get_token(self, prompt: str) -> str:
59+
def get_token(self, prompt: str, token_chars: Optional[Set[str]] = None) -> str:
6060
result = ''
61-
alnum = set(string.ascii_letters + string.digits + ' ' + '\n' + '\r')
61+
if token_chars is None:
62+
token_chars = set(string.ascii_letters + string.digits + ' ' + '\n' + '\r')
6263
while True:
6364
next_char = self.get_next_char(prompt)
64-
if len(result) > 2 and next_char not in alnum or (next_char.strip() == '' and result.strip() != ''):
65+
if len(result) > 2 and next_char not in token_chars or (next_char.strip() == '' and result.strip() != ''):
6566
if not result:
6667
result += next_char
6768
return result

python_autocomplete/serve.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1+
import json
2+
import string
13
import threading
24

35
from flask import Flask, request, jsonify
46

5-
from labml import experiment
7+
from labml import experiment, monit
68
from labml.utils.cache import cache
79
from labml.utils.pytorch import get_modules
810
from python_autocomplete.evaluate import Predictor
911
from python_autocomplete.train import Configs
1012

13+
TOKEN_CHARS = set(string.ascii_letters + string.digits + ' ' + '\n' + '\r' + '_')
14+
1115

1216
def get_predictor():
1317
conf = Configs()
@@ -42,13 +46,16 @@ def autocomplete():
4246
if not prompt:
4347
return jsonify({'success': False})
4448

45-
acquired = lock.acquire(blocking=False)
46-
if acquired:
47-
res = predictor.get_token(prompt)
48-
lock.release()
49-
return jsonify({'success': True, 'prediction': res})
50-
else:
51-
return jsonify({'success': False})
49+
with monit.section('Predict') as s:
50+
acquired = lock.acquire(blocking=False)
51+
if acquired:
52+
res = predictor.get_token(prompt, token_chars=TOKEN_CHARS)
53+
lock.release()
54+
s.message = f'{json.dumps(prompt[-5:])} -> {json.dumps(res)}'
55+
return jsonify({'success': True, 'prediction': res})
56+
else:
57+
monit.fail()
58+
return jsonify({'success': False})
5259

5360

5461
if __name__ == '__main__':

readme.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,32 @@ This repo trains deep learning models on source code.
1919
*Try changing hyper-parameters like model dimensions and number of layers*.
2020
5. Run `evaluate.py` to evaluate the model.
2121

22+
### Trying the VSCode extension
23+
24+
1. Install npm packages
25+
26+
```shell
27+
cd vscode_extension
28+
npm install
29+
```
30+
31+
2. Open the project in vscode
32+
33+
```shell
34+
cd vscode_extension
35+
code .
36+
```
37+
38+
3. Run the extension
39+
40+
```
41+
Run -> Start Debugging
42+
```
43+
44+
This will open another VSCode editor window, with the extension
45+
46+
4. Create or open a python file and start editing!
47+
2248
<p align="center">
2349
<img src="/python-autocomplete.png?raw=true" width="100%" title="Screenshot">
2450
</p>

vscode_extension/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules/
2+
out/
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
"name": "Run Extension",
6+
"type": "extensionHost",
7+
"request": "launch",
8+
"runtimeExecutable": "${execPath}",
9+
"args": [
10+
"--extensionDevelopmentPath=${workspaceFolder}"
11+
],
12+
"outFiles": [
13+
"${workspaceFolder}/out/**/*.js"
14+
],
15+
"preLaunchTask": "npm: watch"
16+
}
17+
]
18+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"editor.insertSpaces": false
3+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"version": "2.0.0",
3+
"tasks": [
4+
{
5+
"type": "npm",
6+
"script": "watch",
7+
"problemMatcher": "$tsc-watch",
8+
"isBackground": true,
9+
"presentation": {
10+
"reveal": "never"
11+
},
12+
"group": {
13+
"kind": "build",
14+
"isDefault": true
15+
}
16+
}
17+
]
18+
}

vscode_extension/package-lock.json

Lines changed: 65 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vscode_extension/package.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"name": "python-autocomplete",
3+
"displayName": "Autocomplete Python (PyTorch) code with a transformer",
4+
"version": "0.0.1",
5+
"publisher": "vpj",
6+
"repository": {
7+
"type": "git",
8+
"url": "https://github.com/lab-ml/python_autocomplete"
9+
},
10+
"bugs": {
11+
"url": "https://github.com/lab-ml/python_autocomplete/issues"
12+
},
13+
"engines": {
14+
"vscode": "^1.32.0"
15+
},
16+
"categories": [
17+
"Other"
18+
],
19+
"activationEvents": [
20+
"*"
21+
],
22+
"main": "./out/extension.js",
23+
"scripts": {
24+
"vscode:prepublish": "npm run compile",
25+
"compile": "tsc -p ./",
26+
"watch": "tsc -watch -p ./"
27+
},
28+
"devDependencies": {
29+
"@types/node": "*",
30+
"@types/vscode": "*",
31+
"typescript": "*"
32+
}
33+
}

vscode_extension/src/extension.ts

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import * as vscode from 'vscode'
2+
import * as https from 'http'
3+
4+
async function fetch(prompt: string) {
5+
const data = JSON.stringify({ prompt: prompt })
6+
7+
return new Promise<any>((resolve, reject) => {
8+
let respone_data = ''
9+
const req = https.request('http://localhost:5000/autocomplete', {
10+
method: 'POST',
11+
headers: {
12+
'Content-Type': 'application/json',
13+
'Content-Length': data.length
14+
}
15+
}, (res) => {
16+
res.on('data', (d) => {
17+
respone_data += d
18+
})
19+
20+
res.on('end', () => {
21+
resolve(JSON.parse(respone_data))
22+
})
23+
})
24+
req.on('error', (error) => {
25+
console.error(error)
26+
reject()
27+
})
28+
req.write(data)
29+
req.end()
30+
})
31+
}
32+
33+
function getPrompt(document: vscode.TextDocument, position: vscode.Position) {
34+
const start = Math.max(0, position.line - 20)
35+
let text = ''
36+
for (let i = start; i < position.line; ++i) {
37+
text += document.lineAt(i).text + '\n'
38+
}
39+
const line = document.lineAt(position).text
40+
text += line.substr(0, position.character)
41+
42+
return text
43+
}
44+
45+
export function activate(context: vscode.ExtensionContext) {
46+
const provider = vscode.languages.registerCompletionItemProvider('python', {
47+
async provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext) {
48+
const prompt = getPrompt(document, position)
49+
let response
50+
51+
try {
52+
response = await fetch(prompt)
53+
} catch(e) {
54+
return []
55+
}
56+
57+
// Failure
58+
if (!response.success) {
59+
return []
60+
}
61+
62+
let prediction: string = response.prediction
63+
64+
// Remove new lines because it's a bit annoying?
65+
let nl = prediction.indexOf('\n')
66+
if (nl !== -1) {
67+
prediction = prediction.substr(0, nl)
68+
}
69+
70+
if (prediction === '') {
71+
// If at end of a line just predict new line, to avoid annoying default vscode predictions
72+
if (nl !== -1) {
73+
const simpleCompletion = new vscode.CompletionItem('\n')
74+
simpleCompletion.kind = vscode.CompletionItemKind.Text
75+
simpleCompletion.command = { command: 'editor.action.triggerSuggest', title: 'Re-trigger completions...' }
76+
return [simpleCompletion]
77+
}
78+
return []
79+
}
80+
81+
// Add any word prefix from text (because thats how vscode works)
82+
let range = document.getWordRangeAtPosition(position)
83+
if (range != null) {
84+
const line = document.lineAt(position).text
85+
let prefix = line.substring(range.start.character, position.character)
86+
prediction = prefix + prediction
87+
}
88+
89+
// Create a completion
90+
const simpleCompletion = new vscode.CompletionItem(prediction)
91+
simpleCompletion.kind = vscode.CompletionItemKind.Text
92+
// Dont trigger autocompletion if we hit a new line
93+
if (nl === -1) {
94+
simpleCompletion.command = { command: 'editor.action.triggerSuggest', title: 'Re-trigger completions...' }
95+
}
96+
97+
return [simpleCompletion]
98+
}
99+
})
100+
101+
context.subscriptions.push(provider)
102+
}

0 commit comments

Comments
 (0)