typeprof 0.15.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +1 -1
- data/Gemfile.lock +4 -4
- data/exe/typeprof +5 -1
- data/lib/typeprof/analyzer.rb +261 -66
- data/lib/typeprof/arguments.rb +1 -0
- data/lib/typeprof/builtin.rb +30 -22
- data/lib/typeprof/cli.rb +22 -2
- data/lib/typeprof/code-range.rb +177 -0
- data/lib/typeprof/config.rb +43 -18
- data/lib/typeprof/container-type.rb +10 -1
- data/lib/typeprof/export.rb +199 -20
- data/lib/typeprof/import.rb +30 -4
- data/lib/typeprof/iseq.rb +504 -200
- data/lib/typeprof/lsp.rb +865 -0
- data/lib/typeprof/method.rb +17 -13
- data/lib/typeprof/type.rb +46 -38
- data/lib/typeprof/utils.rb +18 -1
- data/lib/typeprof/version.rb +1 -1
- data/lib/typeprof.rb +3 -0
- data/smoke/array15.rb +1 -1
- data/smoke/array6.rb +2 -2
- data/smoke/array8.rb +1 -1
- data/smoke/block-args2.rb +3 -3
- data/smoke/block-args3.rb +4 -4
- data/smoke/break2.rb +1 -1
- data/smoke/gvar2.rb +0 -3
- data/smoke/hash-bot.rb +1 -1
- data/smoke/hash4.rb +1 -1
- data/smoke/identifier_keywords.rb +17 -0
- data/smoke/next2.rb +1 -1
- data/smoke/or_raise.rb +18 -0
- data/smoke/parameterizedd-self.rb +2 -2
- data/smoke/pattern-match1.rb +1 -6
- data/smoke/rbs-vars.rb +0 -3
- data/testbed/ao.rb +1 -1
- data/typeprof-lsp +3 -0
- data/typeprof.gemspec +1 -1
- data/vscode/.gitignore +5 -0
- data/vscode/.vscode/launch.json +16 -0
- data/vscode/.vscodeignore +7 -0
- data/vscode/README.md +22 -0
- data/vscode/development.md +31 -0
- data/vscode/package-lock.json +2211 -0
- data/vscode/package.json +71 -0
- data/vscode/sandbox/test.rb +24 -0
- data/vscode/src/extension.ts +285 -0
- data/vscode/tsconfig.json +15 -0
- metadata +20 -5
data/vscode/package.json
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
{
|
2
|
+
"name": "ruby-typeprof",
|
3
|
+
"displayName": "Ruby TypeProf",
|
4
|
+
"version": "0.20.0",
|
5
|
+
"publisher": "mame",
|
6
|
+
"author": {
|
7
|
+
"name": "Yusuke Endoh"
|
8
|
+
},
|
9
|
+
"repository": {
|
10
|
+
"type": "git",
|
11
|
+
"url": "https://github.com/mame/vscode-typeprof"
|
12
|
+
},
|
13
|
+
"license": "MIT",
|
14
|
+
"categories": [
|
15
|
+
"Programming Languages"
|
16
|
+
],
|
17
|
+
"keywords": [
|
18
|
+
"Ruby",
|
19
|
+
"language server"
|
20
|
+
],
|
21
|
+
"engines": {
|
22
|
+
"vscode": "^1.54.0"
|
23
|
+
},
|
24
|
+
"extensionKind": [
|
25
|
+
"workspace"
|
26
|
+
],
|
27
|
+
"activationEvents": [
|
28
|
+
"onLanguage:ruby"
|
29
|
+
],
|
30
|
+
"contributes": {
|
31
|
+
"commands": [
|
32
|
+
{
|
33
|
+
"command": "typeprof.restart",
|
34
|
+
"title": "Restart",
|
35
|
+
"category": "TypeProf"
|
36
|
+
}
|
37
|
+
],
|
38
|
+
"configuration": [
|
39
|
+
{
|
40
|
+
"title": "Ruby TypeProf",
|
41
|
+
"properties": {
|
42
|
+
"typeprof.server.path": {
|
43
|
+
"type": [
|
44
|
+
"null",
|
45
|
+
"string"
|
46
|
+
],
|
47
|
+
"default": null,
|
48
|
+
"description": "Path to typeprof executable. (e.g. /usr/local/bin/bundle)"
|
49
|
+
}
|
50
|
+
}
|
51
|
+
}
|
52
|
+
]
|
53
|
+
},
|
54
|
+
"main": "./out/src/extension",
|
55
|
+
"scripts": {
|
56
|
+
"vscode:prepublish": "tsc -p ./",
|
57
|
+
"compile": "tsc -watch -p ./",
|
58
|
+
"postinstall": "node ./node_modules/vscode/bin/install",
|
59
|
+
"test": "node ./node_modules/vscode/bin/test",
|
60
|
+
"package": "vsce package"
|
61
|
+
},
|
62
|
+
"devDependencies": {
|
63
|
+
"@types/node": "^14.14.37",
|
64
|
+
"typescript": "^4.2.3",
|
65
|
+
"vsce": "^1.96.1",
|
66
|
+
"vscode": "^1.1.37"
|
67
|
+
},
|
68
|
+
"dependencies": {
|
69
|
+
"vscode-languageclient": "^7.0.0"
|
70
|
+
}
|
71
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class C
|
2
|
+
def initialize
|
3
|
+
end
|
4
|
+
|
5
|
+
def get_foo
|
6
|
+
:Foo_foo
|
7
|
+
end
|
8
|
+
|
9
|
+
def get_bar
|
10
|
+
:Foo_bar
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class D
|
15
|
+
def get_foo
|
16
|
+
:Foo2_foo
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
C.new.get_foo
|
21
|
+
C.new.get_bar
|
22
|
+
|
23
|
+
x = rand < 0.5 ? C.new : D.new
|
24
|
+
x.get_foo
|
@@ -0,0 +1,285 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
import * as vscode from "vscode";
|
4
|
+
import {
|
5
|
+
LanguageClient,
|
6
|
+
LanguageClientOptions,
|
7
|
+
ServerOptions,
|
8
|
+
} from "vscode-languageclient/node";
|
9
|
+
import * as net from "net";
|
10
|
+
import * as child_process from "child_process";
|
11
|
+
import { existsSync } from "fs";
|
12
|
+
|
13
|
+
interface Invoking {
|
14
|
+
kind: "invoking";
|
15
|
+
workspaceFolder: vscode.WorkspaceFolder;
|
16
|
+
process: child_process.ChildProcessWithoutNullStreams;
|
17
|
+
}
|
18
|
+
interface Running {
|
19
|
+
kind: "running";
|
20
|
+
workspaceFolder: vscode.WorkspaceFolder;
|
21
|
+
client: LanguageClient;
|
22
|
+
}
|
23
|
+
type State = Invoking | Running;
|
24
|
+
|
25
|
+
const CONFIGURATION_ROOT_SECTION = "typeprof";
|
26
|
+
|
27
|
+
function addToggleButton(context: vscode.ExtensionContext) {
|
28
|
+
let statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
|
29
|
+
statusBarItem.command = "typeprof.toggle";
|
30
|
+
statusBarItem.text = "TypeProf $(eye)";
|
31
|
+
statusBarItem.show();
|
32
|
+
|
33
|
+
const disposable = vscode.commands.registerCommand("typeprof.toggle",
|
34
|
+
(arg0: any, arg1: any, arg2: any, arg3: any) => {
|
35
|
+
if (statusBarItem.text == "TypeProf $(eye)") {
|
36
|
+
statusBarItem.text = "TypeProf $(eye-closed)";
|
37
|
+
vscode.commands.executeCommand("typeprof.disableSignature");
|
38
|
+
}
|
39
|
+
else {
|
40
|
+
statusBarItem.text = "TypeProf $(eye)";
|
41
|
+
vscode.commands.executeCommand("typeprof.enableSignature");
|
42
|
+
}
|
43
|
+
}
|
44
|
+
);
|
45
|
+
|
46
|
+
context.subscriptions.push(disposable);
|
47
|
+
}
|
48
|
+
|
49
|
+
function addJumpToRBS(context: vscode.ExtensionContext) {
|
50
|
+
const disposable = vscode.commands.registerCommand("typeprof.jumpToRBS",
|
51
|
+
(arg0: any, arg1: any, arg2: any, arg3: any) => {
|
52
|
+
const uri0 = vscode.Uri.parse(arg0);
|
53
|
+
const pos0 = new vscode.Position(arg1.line, arg1.character);
|
54
|
+
const uri1 = vscode.Uri.parse(arg2);
|
55
|
+
const pos1 = new vscode.Position(arg3.start.line, arg3.start.character);
|
56
|
+
const pos2 = new vscode.Position(arg3.end.line, arg3.end.character);
|
57
|
+
const range = new vscode.Range(pos1, pos2);
|
58
|
+
const loc = new vscode.Location(uri1, range);
|
59
|
+
vscode.commands.executeCommand("editor.action.peekLocations", uri0, pos0, [loc], "peek");
|
60
|
+
}
|
61
|
+
);
|
62
|
+
|
63
|
+
context.subscriptions.push(disposable);
|
64
|
+
}
|
65
|
+
|
66
|
+
function executeTypeProf(folder: vscode.WorkspaceFolder, arg: String): child_process.ChildProcessWithoutNullStreams {
|
67
|
+
const configuration = vscode.workspace.getConfiguration(CONFIGURATION_ROOT_SECTION);
|
68
|
+
const customServerPath = configuration.get<string | null>("server.path");
|
69
|
+
const cwd = folder.uri.fsPath;
|
70
|
+
|
71
|
+
let cmd: string;
|
72
|
+
if (existsSync(`${cwd}/bin/typeprof`)) {
|
73
|
+
cmd = "./bin/typeprof";
|
74
|
+
}
|
75
|
+
else if (customServerPath) {
|
76
|
+
cmd = customServerPath;
|
77
|
+
}
|
78
|
+
else if (existsSync(`${cwd}/Gemfile`)) {
|
79
|
+
cmd = "bundle exec typeprof";
|
80
|
+
}
|
81
|
+
else {
|
82
|
+
cmd = "typeprof";
|
83
|
+
}
|
84
|
+
cmd = cmd + " " + arg;
|
85
|
+
|
86
|
+
const shell = process.env.SHELL;
|
87
|
+
let typeprof: child_process.ChildProcessWithoutNullStreams;
|
88
|
+
if (shell && (shell.endsWith("bash") || shell.endsWith("zsh") || shell.endsWith("fish"))) {
|
89
|
+
typeprof = child_process.spawn(shell, ["-c", "-l", cmd], { cwd });
|
90
|
+
}
|
91
|
+
else {
|
92
|
+
typeprof = child_process.spawn(cmd, { cwd });
|
93
|
+
}
|
94
|
+
|
95
|
+
return typeprof;
|
96
|
+
}
|
97
|
+
|
98
|
+
function getTypeProfVersion(folder: vscode.WorkspaceFolder, callback: (version: string) => void): child_process.ChildProcessWithoutNullStreams {
|
99
|
+
const typeprof = executeTypeProf(folder, "--version");
|
100
|
+
let output = "";
|
101
|
+
|
102
|
+
typeprof.stdout?.on("data", out => { output += out; });
|
103
|
+
typeprof.stderr?.on("data", out => { console.log(out); });
|
104
|
+
typeprof.on("error", e => {
|
105
|
+
console.info(`typeprof is not supported for this folder: ${folder}`);
|
106
|
+
console.info(`because: ${e}`);
|
107
|
+
});
|
108
|
+
typeprof.on("exit", (code) => {
|
109
|
+
if (code == 0) {
|
110
|
+
console.info(`typeprof version: ${output}`)
|
111
|
+
const str = output.trim();
|
112
|
+
const version = /^typeprof (\d+).(\d+).(\d+)$/.exec(str);
|
113
|
+
if (version) {
|
114
|
+
const major = Number(version[1]);
|
115
|
+
const minor = Number(version[2]);
|
116
|
+
const _teeny = Number(version[3]);
|
117
|
+
if (major >= 1 || (major == 0 && minor >= 20)) {
|
118
|
+
callback(str);
|
119
|
+
}
|
120
|
+
else {
|
121
|
+
console.info(`typeprof version ${str} is too old; please use 0.20.0 or later for IDE feature`);
|
122
|
+
}
|
123
|
+
}
|
124
|
+
else {
|
125
|
+
console.info(`typeprof --version showed unknown message`);
|
126
|
+
}
|
127
|
+
}
|
128
|
+
else {
|
129
|
+
console.info(`failed to invoke typeprof: error code ${code}`);
|
130
|
+
}
|
131
|
+
typeprof.kill()
|
132
|
+
});
|
133
|
+
return typeprof;
|
134
|
+
}
|
135
|
+
|
136
|
+
function getTypeProfStream(folder: vscode.WorkspaceFolder, error: (msg: string) => void):
|
137
|
+
Promise<{ host: string; port: number; pid: number; stop: () => void }>
|
138
|
+
{
|
139
|
+
return new Promise((resolve, reject) => {
|
140
|
+
const typeprof = executeTypeProf(folder, "--lsp");
|
141
|
+
|
142
|
+
let buffer = "";
|
143
|
+
typeprof.stdout.on("data", (data) => {
|
144
|
+
buffer += data;
|
145
|
+
try {
|
146
|
+
const json = JSON.parse(data);
|
147
|
+
json["stop"] = () => typeprof.kill("SIGINT");
|
148
|
+
resolve(json);
|
149
|
+
} catch (err) {}
|
150
|
+
});
|
151
|
+
|
152
|
+
let err = "";
|
153
|
+
typeprof.stderr.on("data", (data) => {
|
154
|
+
err += data;
|
155
|
+
while (true) {
|
156
|
+
const i = err.indexOf("\n");
|
157
|
+
if (i < 0) break;
|
158
|
+
error(err.slice(0, i));
|
159
|
+
err = err.slice(i + 1);
|
160
|
+
}
|
161
|
+
});
|
162
|
+
|
163
|
+
typeprof.on("exit", (code) => reject(`error code ${code}`));
|
164
|
+
});
|
165
|
+
}
|
166
|
+
|
167
|
+
function invokeTypeProf(folder: vscode.WorkspaceFolder): LanguageClient {
|
168
|
+
let client: LanguageClient;
|
169
|
+
|
170
|
+
const reportError = (msg: string) => client.info(msg);
|
171
|
+
|
172
|
+
const serverOptions: ServerOptions = async () => {
|
173
|
+
const { host, port, stop } = await getTypeProfStream(folder, reportError);
|
174
|
+
const socket: net.Socket = net.createConnection(port, host);
|
175
|
+
socket.on("close", (_had_error) => stop());
|
176
|
+
|
177
|
+
return {
|
178
|
+
reader: socket,
|
179
|
+
writer: socket,
|
180
|
+
};
|
181
|
+
};
|
182
|
+
|
183
|
+
const clientOptions: LanguageClientOptions = {
|
184
|
+
documentSelector: [
|
185
|
+
{ scheme: "file", language: "ruby" },
|
186
|
+
{ scheme: "file", language: "rbs" },
|
187
|
+
],
|
188
|
+
synchronize: {
|
189
|
+
fileEvents:
|
190
|
+
vscode.workspace.createFileSystemWatcher("{**/*.rb,**/*.rbs}"),
|
191
|
+
},
|
192
|
+
};
|
193
|
+
|
194
|
+
client = new LanguageClient("Ruby TypeProf", serverOptions, clientOptions);
|
195
|
+
|
196
|
+
return client;
|
197
|
+
}
|
198
|
+
|
199
|
+
const clientSessions: Map<vscode.WorkspaceFolder, State> = new Map();
|
200
|
+
|
201
|
+
function startTypeProf(folder: vscode.WorkspaceFolder) {
|
202
|
+
const showStatus = (msg: string) => vscode.window.setStatusBarMessage(msg, 3000);
|
203
|
+
console.log(`start: ${folder}`);
|
204
|
+
|
205
|
+
const typeprof = getTypeProfVersion(folder, (version) => {
|
206
|
+
if (!version) {
|
207
|
+
showStatus(`Ruby TypeProf is not configured; Try to add "gem 'typeprof'" to Gemfile`);
|
208
|
+
clientSessions.delete(folder);
|
209
|
+
return;
|
210
|
+
}
|
211
|
+
showStatus(`Starting Ruby TypeProf (${version})...`);
|
212
|
+
const client = invokeTypeProf(folder);
|
213
|
+
client.onReady()
|
214
|
+
.then(() => {
|
215
|
+
showStatus("Ruby TypeProf is running");
|
216
|
+
})
|
217
|
+
.catch((e: any) => {
|
218
|
+
showStatus(`Failed to start Ruby TypeProf: ${e}`);
|
219
|
+
});
|
220
|
+
client.start();
|
221
|
+
clientSessions.set(folder, { kind: "running", workspaceFolder: folder, client });
|
222
|
+
});
|
223
|
+
|
224
|
+
clientSessions.set(folder, { kind: "invoking", workspaceFolder: folder, process: typeprof });
|
225
|
+
}
|
226
|
+
|
227
|
+
function stopTypeProf(state: State) {
|
228
|
+
console.log(`stop: ${state.workspaceFolder}`);
|
229
|
+
switch (state.kind) {
|
230
|
+
case "invoking":
|
231
|
+
state.process.kill();
|
232
|
+
|
233
|
+
break;
|
234
|
+
case "running":
|
235
|
+
state.client.stop();
|
236
|
+
break;
|
237
|
+
}
|
238
|
+
clientSessions.delete(state.workspaceFolder);
|
239
|
+
}
|
240
|
+
|
241
|
+
function restartTypeProf() {
|
242
|
+
vscode.workspace.workspaceFolders?.forEach((folder) => {
|
243
|
+
let state = clientSessions.get(folder);
|
244
|
+
if (state) stopTypeProf(state);
|
245
|
+
startTypeProf(folder);
|
246
|
+
});
|
247
|
+
}
|
248
|
+
|
249
|
+
function ensureTypeProf() {
|
250
|
+
if (!vscode.workspace.workspaceFolders) return;
|
251
|
+
|
252
|
+
const activeFolders = new Set(vscode.workspace.workspaceFolders);
|
253
|
+
|
254
|
+
clientSessions.forEach((state) => {
|
255
|
+
if (!activeFolders.has(state.workspaceFolder)) {
|
256
|
+
stopTypeProf(state);
|
257
|
+
}
|
258
|
+
});
|
259
|
+
|
260
|
+
activeFolders.forEach((folder) => {
|
261
|
+
if (folder.uri.scheme === "file" && !clientSessions.has(folder)) {
|
262
|
+
startTypeProf(folder);
|
263
|
+
}
|
264
|
+
});
|
265
|
+
}
|
266
|
+
|
267
|
+
function addRestartCommand(context: vscode.ExtensionContext) {
|
268
|
+
const disposable = vscode.commands.registerCommand("typeprof.restart", () => {
|
269
|
+
restartTypeProf();
|
270
|
+
});
|
271
|
+
context.subscriptions.push(disposable);
|
272
|
+
}
|
273
|
+
|
274
|
+
export function activate(context: vscode.ExtensionContext) {
|
275
|
+
addToggleButton(context);
|
276
|
+
addJumpToRBS(context);
|
277
|
+
addRestartCommand(context)
|
278
|
+
ensureTypeProf();
|
279
|
+
}
|
280
|
+
|
281
|
+
export function deactivate() {
|
282
|
+
clientSessions.forEach((state) => {
|
283
|
+
stopTypeProf(state);
|
284
|
+
});
|
285
|
+
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: typeprof
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.20.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yusuke Endoh
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-10-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rbs
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 1.
|
19
|
+
version: 1.6.2
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 1.
|
26
|
+
version: 1.6.2
|
27
27
|
description: |
|
28
28
|
TypeProf performs a type analysis of non-annotated Ruby code.
|
29
29
|
|
@@ -56,12 +56,14 @@ files:
|
|
56
56
|
- lib/typeprof/block.rb
|
57
57
|
- lib/typeprof/builtin.rb
|
58
58
|
- lib/typeprof/cli.rb
|
59
|
+
- lib/typeprof/code-range.rb
|
59
60
|
- lib/typeprof/config.rb
|
60
61
|
- lib/typeprof/container-type.rb
|
61
62
|
- lib/typeprof/export.rb
|
62
63
|
- lib/typeprof/import.rb
|
63
64
|
- lib/typeprof/insns-def.rb
|
64
65
|
- lib/typeprof/iseq.rb
|
66
|
+
- lib/typeprof/lsp.rb
|
65
67
|
- lib/typeprof/method.rb
|
66
68
|
- lib/typeprof/type.rb
|
67
69
|
- lib/typeprof/utils.rb
|
@@ -217,6 +219,7 @@ files:
|
|
217
219
|
- smoke/hash4.rb
|
218
220
|
- smoke/hash5.rb
|
219
221
|
- smoke/huge_union.rb
|
222
|
+
- smoke/identifier_keywords.rb
|
220
223
|
- smoke/included.rb
|
221
224
|
- smoke/inheritance.rb
|
222
225
|
- smoke/inheritance2.rb
|
@@ -276,6 +279,7 @@ files:
|
|
276
279
|
- smoke/optional1.rb
|
277
280
|
- smoke/optional2.rb
|
278
281
|
- smoke/optional3.rb
|
282
|
+
- smoke/or_raise.rb
|
279
283
|
- smoke/parameterizedd-self.rb
|
280
284
|
- smoke/parameterizedd-self2.rb
|
281
285
|
- smoke/pathname1.rb
|
@@ -395,7 +399,18 @@ files:
|
|
395
399
|
- testbed/goodcheck-Gemfile.lock
|
396
400
|
- tools/coverage.rb
|
397
401
|
- tools/setup-insns-def.rb
|
402
|
+
- typeprof-lsp
|
398
403
|
- typeprof.gemspec
|
404
|
+
- vscode/.gitignore
|
405
|
+
- vscode/.vscode/launch.json
|
406
|
+
- vscode/.vscodeignore
|
407
|
+
- vscode/README.md
|
408
|
+
- vscode/development.md
|
409
|
+
- vscode/package-lock.json
|
410
|
+
- vscode/package.json
|
411
|
+
- vscode/sandbox/test.rb
|
412
|
+
- vscode/src/extension.ts
|
413
|
+
- vscode/tsconfig.json
|
399
414
|
homepage: https://github.com/ruby/typeprof
|
400
415
|
licenses:
|
401
416
|
- MIT
|
@@ -417,7 +432,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
417
432
|
- !ruby/object:Gem::Version
|
418
433
|
version: '0'
|
419
434
|
requirements: []
|
420
|
-
rubygems_version: 3.
|
435
|
+
rubygems_version: 3.3.0.dev
|
421
436
|
signing_key:
|
422
437
|
specification_version: 4
|
423
438
|
summary: TypeProf is a type analysis tool for Ruby code based on abstract interpretation
|