diff --git a/examples/basic/src/recordExample.res b/examples/basic/src/recordExample.res new file mode 100644 index 0000000..b5b5eec --- /dev/null +++ b/examples/basic/src/recordExample.res @@ -0,0 +1,15 @@ +module User = { + type t = {name: string} +} + +module Role = { + type t = {roleName: string} +} + +let doSomething = (u: User.t) => { + Js.log(u.name) +} + +let other = (r: Role.t) => { + Js.log(r.roleName) +} diff --git a/server/analysis_binaries/darwin/rescript-editor-analysis.exe b/server/analysis_binaries/darwin/rescript-editor-analysis.exe index d26e436..edfb781 100755 Binary files a/server/analysis_binaries/darwin/rescript-editor-analysis.exe and b/server/analysis_binaries/darwin/rescript-editor-analysis.exe differ diff --git a/server/analysis_binaries/linux/rescript-editor-analysis.exe b/server/analysis_binaries/linux/rescript-editor-analysis.exe index 3dc4caa..13aebf1 100755 Binary files a/server/analysis_binaries/linux/rescript-editor-analysis.exe and b/server/analysis_binaries/linux/rescript-editor-analysis.exe differ diff --git a/server/analysis_binaries/win32/rescript-editor-analysis.exe b/server/analysis_binaries/win32/rescript-editor-analysis.exe index 534e7ed..804afd5 100755 Binary files a/server/analysis_binaries/win32/rescript-editor-analysis.exe and b/server/analysis_binaries/win32/rescript-editor-analysis.exe differ diff --git a/server/node_modules/anymatch/package.json b/server/node_modules/anymatch/package.json index ca367fd..f9b5284 100644 --- a/server/node_modules/anymatch/package.json +++ b/server/node_modules/anymatch/package.json @@ -45,8 +45,4 @@ "engines": { "node": ">= 8" } - -,"_resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz" -,"_integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==" -,"_from": "anymatch@3.1.2" -} \ No newline at end of file +} diff --git a/server/node_modules/binary-extensions/package.json b/server/node_modules/binary-extensions/package.json index 6ba456a..c4d3641 100644 --- a/server/node_modules/binary-extensions/package.json +++ b/server/node_modules/binary-extensions/package.json @@ -35,8 +35,4 @@ "tsd": "^0.7.2", "xo": "^0.24.0" } - -,"_resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" -,"_integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" -,"_from": "binary-extensions@2.2.0" -} \ No newline at end of file +} diff --git a/server/node_modules/braces/package.json b/server/node_modules/braces/package.json index 008f640..3f52e34 100644 --- a/server/node_modules/braces/package.json +++ b/server/node_modules/braces/package.json @@ -74,8 +74,4 @@ "gulp-format-md" ] } - -,"_resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" -,"_integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==" -,"_from": "braces@3.0.2" -} \ No newline at end of file +} diff --git a/server/node_modules/chokidar/package.json b/server/node_modules/chokidar/package.json index 9f78dff..9ebbb40 100644 --- a/server/node_modules/chokidar/package.json +++ b/server/node_modules/chokidar/package.json @@ -75,8 +75,4 @@ "text" ] } - -,"_resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz" -,"_integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==" -,"_from": "chokidar@3.5.1" -} \ No newline at end of file +} diff --git a/server/node_modules/fill-range/package.json b/server/node_modules/fill-range/package.json index 6aeb617..07d3076 100644 --- a/server/node_modules/fill-range/package.json +++ b/server/node_modules/fill-range/package.json @@ -66,8 +66,4 @@ "reflinks": true } } - -,"_resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" -,"_integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==" -,"_from": "fill-range@7.0.1" -} \ No newline at end of file +} diff --git a/server/node_modules/fsevents/package.json b/server/node_modules/fsevents/package.json index e240b6f..af6da84 100644 --- a/server/node_modules/fsevents/package.json +++ b/server/node_modules/fsevents/package.json @@ -59,8 +59,4 @@ "devDependencies": { "node-gyp": "^6.1.0" } - -,"_resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" -,"_integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==" -,"_from": "fsevents@2.3.2" -} \ No newline at end of file +} diff --git a/server/node_modules/glob-parent/package.json b/server/node_modules/glob-parent/package.json index 20b913e..125c971 100644 --- a/server/node_modules/glob-parent/package.json +++ b/server/node_modules/glob-parent/package.json @@ -45,8 +45,4 @@ "base", "wildcard" ] - -,"_resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" -,"_integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==" -,"_from": "glob-parent@5.1.2" -} \ No newline at end of file +} diff --git a/server/node_modules/is-binary-path/package.json b/server/node_modules/is-binary-path/package.json index 4998949..a8d005a 100644 --- a/server/node_modules/is-binary-path/package.json +++ b/server/node_modules/is-binary-path/package.json @@ -37,8 +37,4 @@ "tsd": "^0.7.2", "xo": "^0.24.0" } - -,"_resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" -,"_integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==" -,"_from": "is-binary-path@2.1.0" -} \ No newline at end of file +} diff --git a/server/node_modules/is-extglob/package.json b/server/node_modules/is-extglob/package.json index 956af58..7a90836 100644 --- a/server/node_modules/is-extglob/package.json +++ b/server/node_modules/is-extglob/package.json @@ -66,8 +66,4 @@ "reflinks": true } } - -,"_resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" -,"_integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" -,"_from": "is-extglob@2.1.1" -} \ No newline at end of file +} diff --git a/server/node_modules/is-glob/package.json b/server/node_modules/is-glob/package.json index 2ca9e34..806000d 100644 --- a/server/node_modules/is-glob/package.json +++ b/server/node_modules/is-glob/package.json @@ -78,8 +78,4 @@ "vinyl" ] } - -,"_resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz" -,"_integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==" -,"_from": "is-glob@4.0.1" -} \ No newline at end of file +} diff --git a/server/node_modules/is-number/package.json b/server/node_modules/is-number/package.json index 0023617..3715072 100644 --- a/server/node_modules/is-number/package.json +++ b/server/node_modules/is-number/package.json @@ -79,8 +79,4 @@ "reflinks": true } } - -,"_resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" -,"_integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" -,"_from": "is-number@7.0.0" -} \ No newline at end of file +} diff --git a/server/node_modules/normalize-path/package.json b/server/node_modules/normalize-path/package.json index 73ab0c9..ad61098 100644 --- a/server/node_modules/normalize-path/package.json +++ b/server/node_modules/normalize-path/package.json @@ -74,8 +74,4 @@ "reflinks": true } } - -,"_resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" -,"_integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" -,"_from": "normalize-path@3.0.0" -} \ No newline at end of file +} diff --git a/server/node_modules/picomatch/package.json b/server/node_modules/picomatch/package.json index 844bd4d..9be4556 100644 --- a/server/node_modules/picomatch/package.json +++ b/server/node_modules/picomatch/package.json @@ -78,8 +78,4 @@ "picomatch" ] } - -,"_resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz" -,"_integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==" -,"_from": "picomatch@2.2.3" -} \ No newline at end of file +} diff --git a/server/node_modules/readdirp/package.json b/server/node_modules/readdirp/package.json index f8392fa..5a35475 100644 --- a/server/node_modules/readdirp/package.json +++ b/server/node_modules/readdirp/package.json @@ -119,8 +119,4 @@ ] } } - -,"_resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz" -,"_integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==" -,"_from": "readdirp@3.5.0" -} \ No newline at end of file +} diff --git a/server/node_modules/to-regex-range/package.json b/server/node_modules/to-regex-range/package.json index fe98f06..4ef194f 100644 --- a/server/node_modules/to-regex-range/package.json +++ b/server/node_modules/to-regex-range/package.json @@ -85,8 +85,4 @@ ] } } - -,"_resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" -,"_integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==" -,"_from": "to-regex-range@5.0.1" -} \ No newline at end of file +} diff --git a/server/node_modules/vscode-jsonrpc/package.json b/server/node_modules/vscode-jsonrpc/package.json index 082147c..070ef85 100644 --- a/server/node_modules/vscode-jsonrpc/package.json +++ b/server/node_modules/vscode-jsonrpc/package.json @@ -26,8 +26,4 @@ "clean": "node ../node_modules/rimraf/bin.js lib", "preversion": "npm test" } - -,"_resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz" -,"_integrity": "sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A==" -,"_from": "vscode-jsonrpc@5.0.1" -} \ No newline at end of file +} diff --git a/server/node_modules/vscode-languageserver-protocol/node_modules/vscode-jsonrpc/package.json b/server/node_modules/vscode-languageserver-protocol/node_modules/vscode-jsonrpc/package.json index a0f9472..8c7becf 100644 --- a/server/node_modules/vscode-languageserver-protocol/node_modules/vscode-jsonrpc/package.json +++ b/server/node_modules/vscode-languageserver-protocol/node_modules/vscode-jsonrpc/package.json @@ -38,8 +38,4 @@ "webpack:test": "node ../build/bin/webpack --mode none --config ./src/browser/test/webpack.config.js", "webpack:test:silent": "node ../build/bin/webpack --no-stats --mode none --config ./src/browser/test/webpack.config.js" } - -,"_resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz" -,"_integrity": "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==" -,"_from": "vscode-jsonrpc@6.0.0" -} \ No newline at end of file +} diff --git a/server/node_modules/vscode-languageserver-protocol/package.json b/server/node_modules/vscode-languageserver-protocol/package.json index 799963f..4f39d92 100644 --- a/server/node_modules/vscode-languageserver-protocol/package.json +++ b/server/node_modules/vscode-languageserver-protocol/package.json @@ -34,8 +34,4 @@ "webpack:test": "cd ../types && npm run compile:esm && cd ../protocol && node ../build/bin/webpack --mode none --config ./src/browser/test/webpack.config.js", "webpack:test:silent": "cd ../types && npm run compile:esm && cd ../protocol && node ../build/bin/webpack --no-stats --mode none --config ./src/browser/test/webpack.config.js" } - -,"_resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz" -,"_integrity": "sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==" -,"_from": "vscode-languageserver-protocol@3.16.0" -} \ No newline at end of file +} diff --git a/server/node_modules/vscode-languageserver-types/package.json b/server/node_modules/vscode-languageserver-types/package.json index 073175e..8c8e202 100644 --- a/server/node_modules/vscode-languageserver-types/package.json +++ b/server/node_modules/vscode-languageserver-types/package.json @@ -25,8 +25,4 @@ "test": "node ../node_modules/mocha/bin/_mocha", "preversion": "npm test" } - -,"_resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz" -,"_integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==" -,"_from": "vscode-languageserver-types@3.16.0" -} \ No newline at end of file +} diff --git a/server/node_modules/vscode-languageserver/package.json b/server/node_modules/vscode-languageserver/package.json index 701cc36..2610f88 100644 --- a/server/node_modules/vscode-languageserver/package.json +++ b/server/node_modules/vscode-languageserver/package.json @@ -32,8 +32,4 @@ "test": "node ../node_modules/mocha/bin/_mocha", "preversion": "npm test" } - -,"_resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-7.0.0.tgz" -,"_integrity": "sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw==" -,"_from": "vscode-languageserver@7.0.0" -} \ No newline at end of file +} diff --git a/server/out/constants.js b/server/out/constants.js index cda52d7..ed1d586 100644 --- a/server/out/constants.js +++ b/server/out/constants.js @@ -19,7 +19,7 @@ var __importStar = (this && this.__importStar) || function (mod) { return result; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.startBuildAction = exports.resiExt = exports.resExt = exports.compilerLogPartialPath = exports.bsconfigPartialPath = exports.bsbLock = exports.bsbNodePartialPath = exports.rescriptNodePartialPath = exports.analysisProdPath = exports.analysisDevPath = exports.bscNativePartialPath = exports.bscNativeReScriptPartialPath = exports.jsonrpcVersion = void 0; +exports.startBuildAction = exports.cmiExt = exports.resiExt = exports.resExt = exports.compilerLogPartialPath = exports.compilerDirPartialPath = exports.bsconfigPartialPath = exports.bsbLock = exports.bsbNodePartialPath = exports.rescriptNodePartialPath = exports.analysisProdPath = exports.analysisDevPath = exports.bscNativePartialPath = exports.bscNativeReScriptPartialPath = exports.jsonrpcVersion = void 0; const path = __importStar(require("path")); // See https://microsoft.github.io/language-server-protocol/specification Abstract Message // version is fixed to 2.0 @@ -33,8 +33,10 @@ exports.rescriptNodePartialPath = path.join("node_modules", ".bin", "rescript"); exports.bsbNodePartialPath = path.join("node_modules", ".bin", "bsb"); exports.bsbLock = ".bsb.lock"; exports.bsconfigPartialPath = "bsconfig.json"; +exports.compilerDirPartialPath = path.join("lib", "bs"); exports.compilerLogPartialPath = path.join("lib", "bs", ".compiler.log"); exports.resExt = ".res"; exports.resiExt = ".resi"; +exports.cmiExt = ".cmi"; exports.startBuildAction = "Start Build"; //# sourceMappingURL=constants.js.map \ No newline at end of file diff --git a/server/out/server.js b/server/out/server.js index dd1a59b..3e0b1b8 100644 --- a/server/out/server.js +++ b/server/out/server.js @@ -47,6 +47,7 @@ let projectsFiles = new Map(); // ^ caching AND states AND distributed system. Why does LSP has to be stupid like this // will be properly defined later depending on the mode (stdio/node-rpc) let send = (_) => { }; +let createInterfaceRequest = new v.RequestType("rescript-vscode.create_interface"); let sendUpdatedDiagnostics = () => { projectsFiles.forEach(({ filesWithDiagnostics }, projectRootPath) => { let content = fs_1.default.readFileSync(path.join(projectRootPath, c.compilerLogPartialPath), { encoding: "utf-8" }); @@ -216,6 +217,240 @@ else { send = (msg) => process_1.default.send(msg); process_1.default.on("message", onMessage); } +function hover(msg) { + let params = msg.params; + let filePath = url_1.fileURLToPath(params.textDocument.uri); + let response = utils.runAnalysisCommand(filePath, ["hover", filePath, params.position.line, params.position.character], msg); + return response; +} +function definition(msg) { + // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition + let params = msg.params; + let filePath = url_1.fileURLToPath(params.textDocument.uri); + let response = utils.runAnalysisCommand(filePath, ["definition", filePath, params.position.line, params.position.character], msg); + return response; +} +function references(msg) { + // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references + let params = msg.params; + let filePath = url_1.fileURLToPath(params.textDocument.uri); + let result = utils.getReferencesForPosition(filePath, params.position); + let response = { + jsonrpc: c.jsonrpcVersion, + id: msg.id, + result, + // error: code and message set in case an exception happens during the definition request. + }; + return response; +} +function rename(msg) { + // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rename + let params = msg.params; + let filePath = url_1.fileURLToPath(params.textDocument.uri); + let locations = utils.getReferencesForPosition(filePath, params.position); + let result; + if (locations === null) { + result = null; + } + else { + let changes = {}; + locations.forEach(({ uri, range }) => { + let textEdit = { range, newText: params.newName }; + if (uri in changes) { + changes[uri].push(textEdit); + } + else { + changes[uri] = [textEdit]; + } + }); + result = { changes }; + } + let response = { + jsonrpc: c.jsonrpcVersion, + id: msg.id, + result, + }; + return response; +} +function documentSymbol(msg) { + // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol + let params = msg.params; + let filePath = url_1.fileURLToPath(params.textDocument.uri); + let response = utils.runAnalysisCommand(filePath, ["documentSymbol", filePath], msg); + return response; +} +function completion(msg) { + let params = msg.params; + let filePath = url_1.fileURLToPath(params.textDocument.uri); + let code = getOpenedFileContent(params.textDocument.uri); + let tmpname = utils.createFileInTempDir(); + fs_1.default.writeFileSync(tmpname, code, { encoding: "utf-8" }); + let response = utils.runAnalysisCommand(filePath, [ + "completion", + filePath, + params.position.line, + params.position.character, + tmpname, + ], msg); + fs_1.default.unlink(tmpname, () => null); + return response; +} +function format(msg) { + // technically, a formatting failure should reply with the error. Sadly + // the LSP alert box for these error replies sucks (e.g. doesn't actually + // display the message). In order to signal the client to display a proper + // alert box (sometime with actionable buttons), we need to first send + // back a fake success message (because each request mandates a + // response), then right away send a server notification to display a + // nicer alert. Ugh. + let fakeSuccessResponse = { + jsonrpc: c.jsonrpcVersion, + id: msg.id, + result: [], + }; + let params = msg.params; + let filePath = url_1.fileURLToPath(params.textDocument.uri); + let extension = path.extname(params.textDocument.uri); + if (extension !== c.resExt && extension !== c.resiExt) { + let params = { + type: p.MessageType.Error, + message: `Not a ${c.resExt} or ${c.resiExt} file. Cannot format it.`, + }; + let response = { + jsonrpc: c.jsonrpcVersion, + method: "window/showMessage", + params: params, + }; + return [fakeSuccessResponse, response]; + } + else { + // See comment on findBscNativeDirOfFile for why we need + // to recursively search for bsc.exe upward + let bscNativePath = utils.findBscNativeOfFile(filePath); + if (bscNativePath === null) { + let params = { + type: p.MessageType.Error, + message: `Cannot find a nearby bsc.exe in rescript or bs-platform. It's needed for formatting.`, + }; + let response = { + jsonrpc: c.jsonrpcVersion, + method: "window/showMessage", + params: params, + }; + return [fakeSuccessResponse, response]; + } + else { + // code will always be defined here, even though technically it can be undefined + let code = getOpenedFileContent(params.textDocument.uri); + let formattedResult = utils.formatUsingValidBscNativePath(code, bscNativePath, extension === c.resiExt); + if (formattedResult.kind === "success") { + let max = formattedResult.result.length; + let result = [ + { + range: { + start: { line: 0, character: 0 }, + end: { line: max, character: max }, + }, + newText: formattedResult.result, + }, + ]; + let response = { + jsonrpc: c.jsonrpcVersion, + id: msg.id, + result: result, + }; + return [response]; + } + else { + // let the diagnostics logic display the updated syntax errors, + // from the build. + // Again, not sending the actual errors. See fakeSuccessResponse + // above for explanation + return [fakeSuccessResponse]; + } + } + } +} +function createInterface(msg) { + let params = msg.params; + let extension = path.extname(params.uri); + let filePath = url_1.fileURLToPath(params.uri); + let bscNativePath = utils.findBscNativeOfFile(filePath); + let projDir = utils.findProjectRootOfFile(filePath); + let code = getOpenedFileContent(params.uri); + let isReactComponent = code.includes("@react.component"); + if (bscNativePath === null || projDir === null) { + let params = { + type: p.MessageType.Error, + message: `Cannot find a nearby bsc.exe to generate the interface file.`, + }; + let response = { + jsonrpc: c.jsonrpcVersion, + method: "window/showMessage", + params: params, + }; + return response; + } + if (extension !== c.resExt) { + let params = { + type: p.MessageType.Error, + message: `Not a ${c.resExt} file. Cannot create an interface for it.`, + }; + let response = { + jsonrpc: c.jsonrpcVersion, + method: "window/showMessage", + params: params, + }; + return response; + } + if (isReactComponent) { + let params = { + type: p.MessageType.Error, + message: `Creating an interface with @react.component is not currently supported.`, + }; + let response = { + jsonrpc: c.jsonrpcVersion, + method: "window/showMessage", + params: params, + }; + return response; + } + let cmiPartialPath = utils.replaceFileExtension(filePath.split(projDir)[1], c.cmiExt); + let cmiPath = path.join(projDir, c.compilerDirPartialPath, cmiPartialPath); + let cmiAvailable = fs_1.default.existsSync(cmiPath); + if (!cmiAvailable) { + let params = { + type: p.MessageType.Error, + message: `No compiled interface file found. Please compile your project first.`, + }; + let response = { + jsonrpc: c.jsonrpcVersion, + method: "window/showMessage", + params, + }; + return response; + } + let intfResult = utils.createInterfaceFileUsingValidBscExePath(filePath, cmiPath, bscNativePath); + if (intfResult.kind === "success") { + let response = { + jsonrpc: c.jsonrpcVersion, + id: msg.id, + result: intfResult.result, + }; + return response; + } + else { + let response = { + jsonrpc: c.jsonrpcVersion, + id: msg.id, + error: { + code: m.ErrorCodes.InternalError, + message: "Unable to create interface file.", + }, + }; + return response; + } +} function onMessage(msg) { if (m.isNotificationMessage(msg)) { // notification message, aka the client ends it and doesn't want a reply @@ -285,6 +520,7 @@ function onMessage(msg) { hoverProvider: true, definitionProvider: true, referencesProvider: true, + renameProvider: true, documentSymbolProvider: false, completionProvider: { triggerCharacters: [".", ">", "@", "~"] }, }, @@ -333,172 +569,29 @@ function onMessage(msg) { } } else if (msg.method === p.HoverRequest.method) { - let params = msg.params; - let filePath = url_1.fileURLToPath(params.textDocument.uri); - let result = utils.runAnalysisAfterSanityCheck(filePath, [ - "hover", - filePath, - params.position.line, - params.position.character, - ]); - let hoverResponse = { - jsonrpc: c.jsonrpcVersion, - id: msg.id, - // type result = Hover | null - // type Hover = {contents: MarkedString | MarkedString[] | MarkupContent, range?: Range} - result, - }; - send(hoverResponse); + send(hover(msg)); } else if (msg.method === p.DefinitionRequest.method) { - // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition - let params = msg.params; - let filePath = url_1.fileURLToPath(params.textDocument.uri); - let result = utils.runAnalysisAfterSanityCheck(filePath, [ - "definition", - filePath, - params.position.line, - params.position.character, - ]); - let definitionResponse = { - jsonrpc: c.jsonrpcVersion, - id: msg.id, - result, - // error: code and message set in case an exception happens during the definition request. - }; - send(definitionResponse); + send(definition(msg)); } else if (msg.method === p.ReferencesRequest.method) { - // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references - let params = msg.params; - let filePath = url_1.fileURLToPath(params.textDocument.uri); - let result = utils.runAnalysisAfterSanityCheck(filePath, [ - "references", - filePath, - params.position.line, - params.position.character, - ]); - let definitionResponse = { - jsonrpc: c.jsonrpcVersion, - id: msg.id, - result, - // error: code and message set in case an exception happens during the definition request. - }; - send(definitionResponse); + send(references(msg)); + } + else if (msg.method === p.RenameRequest.method) { + send(rename(msg)); } else if (msg.method === p.DocumentSymbolRequest.method) { - // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol - let params = msg.params; - let filePath = url_1.fileURLToPath(params.textDocument.uri); - let result = utils.runAnalysisAfterSanityCheck(filePath, [ - "documentSymbol", - filePath, - ]); - let definitionResponse = { - jsonrpc: c.jsonrpcVersion, - id: msg.id, - result, - }; - send(definitionResponse); + send(documentSymbol(msg)); } else if (msg.method === p.CompletionRequest.method) { - let params = msg.params; - let filePath = url_1.fileURLToPath(params.textDocument.uri); - let code = getOpenedFileContent(params.textDocument.uri); - let tmpname = utils.createFileInTempDir(); - fs_1.default.writeFileSync(tmpname, code, { encoding: "utf-8" }); - let result = utils.runAnalysisAfterSanityCheck(filePath, [ - "completion", - filePath, - params.position.line, - params.position.character, - tmpname, - ]); - fs_1.default.unlink(tmpname, () => null); - let completionResponse = { - jsonrpc: c.jsonrpcVersion, - id: msg.id, - result, - }; - send(completionResponse); + send(completion(msg)); } else if (msg.method === p.DocumentFormattingRequest.method) { - // technically, a formatting failure should reply with the error. Sadly - // the LSP alert box for these error replies sucks (e.g. doesn't actually - // display the message). In order to signal the client to display a proper - // alert box (sometime with actionable buttons), we need to first send - // back a fake success message (because each request mandates a - // response), then right away send a server notification to display a - // nicer alert. Ugh. - let fakeSuccessResponse = { - jsonrpc: c.jsonrpcVersion, - id: msg.id, - result: [], - }; - let params = msg.params; - let filePath = url_1.fileURLToPath(params.textDocument.uri); - let extension = path.extname(params.textDocument.uri); - if (extension !== c.resExt && extension !== c.resiExt) { - let params = { - type: p.MessageType.Error, - message: `Not a ${c.resExt} or ${c.resiExt} file. Cannot format it.`, - }; - let response = { - jsonrpc: c.jsonrpcVersion, - method: "window/showMessage", - params: params, - }; - send(fakeSuccessResponse); - send(response); - } - else { - // See comment on findBscNativeDirOfFile for why we need - // to recursively search for bsc.exe upward - let bscNativePath = utils.findBscNativeOfFile(filePath); - if (bscNativePath === null) { - let params = { - type: p.MessageType.Error, - message: `Cannot find a nearby bsc.exe in rescript or bs-platform. It's needed for formatting.`, - }; - let response = { - jsonrpc: c.jsonrpcVersion, - method: "window/showMessage", - params: params, - }; - send(fakeSuccessResponse); - send(response); - } - else { - // code will always be defined here, even though technically it can be undefined - let code = getOpenedFileContent(params.textDocument.uri); - let formattedResult = utils.formatUsingValidBscNativePath(code, bscNativePath, extension === c.resiExt); - if (formattedResult.kind === "success") { - let max = formattedResult.result.length; - let result = [ - { - range: { - start: { line: 0, character: 0 }, - end: { line: max, character: max }, - }, - newText: formattedResult.result, - }, - ]; - let response = { - jsonrpc: c.jsonrpcVersion, - id: msg.id, - result: result, - }; - send(response); - } - else { - // let the diagnostics logic display the updated syntax errors, - // from the build. - // Again, not sending the actual errors. See fakeSuccessResponse - // above for explanation - send(fakeSuccessResponse); - } - } - } + let responses = format(msg); + responses.forEach((response) => send(response)); + } + else if (msg.method === createInterfaceRequest.method) { + send(createInterface(msg)); } else { let response = { @@ -537,4 +630,4 @@ function onMessage(msg) { } } } -//# sourceMappingURL=server.js.map +//# sourceMappingURL=server.js.map \ No newline at end of file diff --git a/server/out/utils.js b/server/out/utils.js index ee15679..27f789b 100644 --- a/server/out/utils.js +++ b/server/out/utils.js @@ -22,7 +22,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.parseCompilerLogOutput = exports.runBuildWatcherUsingValidBuildPath = exports.runAnalysisAfterSanityCheck = exports.formatUsingValidBscNativePath = exports.findNodeBuildOfProjectRoot = exports.findBscNativeOfFile = exports.findProjectRootOfFile = exports.createFileInTempDir = void 0; +exports.parseCompilerLogOutput = exports.runBuildWatcherUsingValidBuildPath = exports.createInterfaceFileUsingValidBscExePath = exports.replaceFileExtension = exports.getReferencesForPosition = exports.runAnalysisCommand = exports.runAnalysisAfterSanityCheck = exports.formatUsingValidBscNativePath = exports.findNodeBuildOfProjectRoot = exports.findBscNativeOfFile = exports.findProjectRootOfFile = exports.createFileInTempDir = void 0; const c = __importStar(require("./constants")); const childProcess = __importStar(require("child_process")); const path = __importStar(require("path")); @@ -150,6 +150,50 @@ let runAnalysisAfterSanityCheck = (filePath, args) => { return JSON.parse(stdout.toString()); }; exports.runAnalysisAfterSanityCheck = runAnalysisAfterSanityCheck; +let runAnalysisCommand = (filePath, args, msg) => { + let result = exports.runAnalysisAfterSanityCheck(filePath, args); + let response = { + jsonrpc: c.jsonrpcVersion, + id: msg.id, + result, + }; + return response; +}; +exports.runAnalysisCommand = runAnalysisCommand; +let getReferencesForPosition = (filePath, position) => exports.runAnalysisAfterSanityCheck(filePath, [ + "references", + filePath, + position.line, + position.character, +]); +exports.getReferencesForPosition = getReferencesForPosition; +let replaceFileExtension = (filePath, ext) => { + let name = path.basename(filePath, path.extname(filePath)); + return path.format({ dir: path.dirname(filePath), name, ext }); +}; +exports.replaceFileExtension = replaceFileExtension; +let createInterfaceFileUsingValidBscExePath = (filePath, cmiPath, bscExePath) => { + try { + let resiString = childProcess.execFileSync(bscExePath, [ + "-color", + "never", + cmiPath, + ]); + let resiPath = exports.replaceFileExtension(filePath, c.resiExt); + fs_1.default.writeFileSync(resiPath, resiString, { encoding: "utf-8" }); + return { + kind: "success", + result: "Interface successfully created.", + }; + } + catch (e) { + return { + kind: "error", + error: e.message, + }; + } +}; +exports.createInterfaceFileUsingValidBscExePath = createInterfaceFileUsingValidBscExePath; let runBuildWatcherUsingValidBuildPath = (buildPath, isRescript, projectRootPath) => { let cwdEnv = { cwd: projectRootPath, @@ -225,8 +269,8 @@ exports.runBuildWatcherUsingValidBuildPath = runBuildWatcherUsingValidBuildPath; #Done(1600519680836) */ // parser helpers -let normalizeFileForWindows = (file) => { - return process.platform === "win32" ? `file:\\\\\\${file}` : file; +let pathToURI = (file) => { + return process.platform === "win32" ? `file:\\\\\\${file}` : `file://${file}`; }; let parseFileAndRange = (fileAndRange) => { // https://github.com/rescript-lang/rescript-compiler/blob/0a3f4bb32ca81e89cefd5a912b8795878836f883/jscomp/super_errors/super_location.ml#L15-L25 @@ -250,7 +294,7 @@ let parseFileAndRange = (fileAndRange) => { if (match === null) { // no location! Though LSP insist that we provide at least a dummy location return { - file: normalizeFileForWindows(trimmedFileAndRange), + file: pathToURI(trimmedFileAndRange), range: { start: { line: 0, character: 0 }, end: { line: 0, character: 0 }, @@ -285,7 +329,7 @@ let parseFileAndRange = (fileAndRange) => { }; } return { - file: normalizeFileForWindows(file), + file: pathToURI(file), range, }; }; diff --git a/server/package.json b/server/package.json index 902b21e..e60c22c 100644 --- a/server/package.json +++ b/server/package.json @@ -1,7 +1,7 @@ { "name": "rescript-language-server", "description": "ReScript's language-server", - "version": "1.1.1", + "version": "1.1.2", "author": "chenglou", "license": "MIT", "engines": {