on master help sign in
Added env generators
2eda210iko
Haskell Language Server.novaextension/Scripts/download.js+11−23
@@ -5,6 +5,8 @@ const {
5const {
5const {
6 readStream,
6 readStream,
7 getStorage,
7 getStorage,
8+ call,
9+ hasCustomEnv
8} = require("utils");
10} = require("utils");
9
11
10exports.getHLS = getHLS;
12exports.getHLS = getHLS;
@@ -147,8 +149,12 @@ async function getHLS() {
147 return explicitPath
149 return explicitPath
148 }
150 }
149
151
150− const version = await getGHCVersion();
151 const localHLSPath = await which("haskell-language-server").catch((_) => null)
152 const localHLSPath = await which("haskell-language-server").catch((_) => null)
153+ if (hasCustomEnv() && localHLSPath) {
154+ return localHLSPath
155+ }
156+
157+ const version = await getGHCVersion();
152 if (localHLSPath) {
158 if (localHLSPath) {
153 sendNotification("Found HLS", "Found haskell-language-server in PATH. Checking if it is the right version.")
159 sendNotification("Found HLS", "Found haskell-language-server in PATH. Checking if it is the right version.")
154 const v = await getHLSVersion(localHLSPath)
160 const v = await getHLSVersion(localHLSPath)
@@ -175,35 +181,17 @@ function notifyToolDownloading(name) {
175}
181}
176
182
177function getHLSVersion(path) {
183function getHLSVersion(path) {
178− return call(new Process(path, {
184+ return call(path, {
179 args: ["--version"],
185 args: ["--version"],
180− }))
186+ })
181 .then((x) => x.match(/\(GHC: (\S+)\)/)[1].trim())
187 .then((x) => x.match(/\(GHC: (\S+)\)/)[1].trim())
182 .catch((err) => sendPermanentNotification("HLS version check failed", err))
188 .catch((err) => sendPermanentNotification("HLS version check failed", err))
183}
189}
184
190
185function which(name) {
191function which(name) {
186− return call(new Process("/usr/bin/which", {
192+ return call("/usr/bin/which", {
187 args: [name],
193 args: [name],
188 shell: true,
194 shell: true,
189− })).then((p) => p.trim())
195+ }).then((p) => p.trim())
190}
196}
191
197
192−function call(process) {
193− const promise = new Promise((resolve, reject) => {
194− process.onDidExit((exitCode) => {
195− console.log(exitCode)
196− if (exitCode == 0) {
197− readStream(process.stdout).then((x) => {
198− resolve(x);
199− });
200− } else {
201− readStream(process.stderr).then((x) => {
202− reject(x)
203− });
204− }
205− });
206− })
207− process.start()
208− return promise
209−}
Haskell Language Server.novaextension/Scripts/main.js+5−6
@@ -9,8 +9,9 @@ const {
9 getHLSConfig,
9 getHLSConfig,
10} = require("configuration");
10} = require("configuration");
11const {
11const {
12− getFileName,
12+ getFileName, createEnv, getEnv
13} = require("utils");
13} = require("utils");
14+
14exports.activate = function () {
15exports.activate = function () {
15 nova.commands.register("stop", (_workspace) => {
16 nova.commands.register("stop", (_workspace) => {
16 stopServer();
17 stopServer();
@@ -107,10 +108,12 @@ function applyAllLSPEdits(edits, text) {
107}
108}
108let lspClient = null;
109let lspClient = null;
109async function startServer() {
110async function startServer() {
111+ await createEnv()
110 stopServer();
112 stopServer();
111 const path = await getServerPath();
113 const path = await getServerPath();
114+ const env = await getEnv()
112 const serverOptions = {
115 const serverOptions = {
113− path: path,
116+ path, env,
114 args: ["lsp", "--cwd", nova.workspace.path],
117 args: ["lsp", "--cwd", nova.workspace.path],
115 };
118 };
116 const clientOptions = {
119 const clientOptions = {
@@ -137,7 +140,3 @@ function stopServer() {
137function getServerPath() {
140function getServerPath() {
138 return getHLS();
141 return getHLS();
139}
142}
140−
141−// function getLogPath() {
142−// return getStorage() + "/haskell-language-server.log";
143−// }
Haskell Language Server.novaextension/Scripts/utils.js+90−1
@@ -1,3 +1,8 @@
1+const {
2+ sendNotification,
3+ sendPermanentNotification,
4+} = require("notifications");
5+
1function readStream(readableStream) {
6function readStream(readableStream) {
2 return new Promise((resolve, _reject) => {
7 return new Promise((resolve, _reject) => {
3 const reader = readableStream.getReader();
8 const reader = readableStream.getReader();
@@ -48,4 +53,88 @@ function traceRaw(x) {
48 return x;
53 return x;
49}
54}
50
55
56+async function call(path, args) {
57+ const env = await getEnv()
58+ const process = new Process(path, {...args, ...{env}});
59+ const promise = new Promise((resolve, reject) => {
60+ process.onDidExit((exitCode) => {
61+ console.log(exitCode)
62+ if (exitCode == 0) {
63+ readStream(process.stdout).then((x) => {
64+ resolve(x);
65+ });
66+ } else {
67+ readStream(process.stderr).then((x) => {
68+ reject(x)
69+ });
70+ }
71+ });
72+ })
73+ process.start()
74+ return promise
75+}
76+
77+
78+function parseEnvs(x) {
79+ const env = {}
80+ for (line of x.split("\n")) {
81+ const res = line.match(/(.*?)=(.*)/)
82+ if (res) {
83+ env[res[1]] = res[2]
84+ }
85+ }
86+
87+ console.log(JSON.stringify(env))
88+
89+ return env
90+}
91+
92+let envCache = null
93+
94+async function createEnv() {
95+ const generator = nova.workspace.config.get("env-generator")
96+ if (generator) {
97+ const [prog, ...args] = generator.split(/\s+/)
98+ sendNotification("Generating environment", "Executing " + generator + ".")
99+ const process = new Process(prog, {
100+ shell: true,
101+ args,
102+ cwd: nova.workspace.path
103+ });
104+ const promise = new Promise((resolve, reject) => {
105+ process.onDidExit((exitCode) => {
106+ console.log(exitCode)
107+ if (exitCode == 0) {
108+ readStream(process.stdout).then((x) => {
109+ resolve(x);
110+ });
111+ } else {
112+ readStream(process.stderr).then((x) => {
113+ reject(x)
114+ });
115+ }
116+ });
117+ })
118+ process.start()
119+
120+ const envStr = await promise
121+ .catch((err) => sendPermanentNotification("Generating environment failed", err))
122+ envCache = parseEnvs(envStr)
123+ }
124+}
125+
126+async function getEnv() {
127+ console.log(JSON.stringify(envCache))
128+ if (!envCache) {
129+ await createEnv()
130+ }
131+ return envCache
132+}
133+
134+
135+function hasCustomEnv() {
136+ return (nova.workspace.config.get("env-generator") !== null)
137+}
138+
51−module.exports = { readStream, getStorage, getFileName, trace, traceRaw };
139+module.exports = { readStream, getStorage, getFileName, trace, traceRaw, call, createEnv, hasCustomEnv, getEnv };
140+
Haskell Language Server.novaextension/extension.json+8
@@ -20,6 +20,14 @@
20 "type": "path",
20 "type": "path",
21 "placeholder": "/usr/local/bin/example"
21 "placeholder": "/usr/local/bin/example"
22 },
22 },
23+ {
24+ "key": "env-generator",
25+ "title": "Environment generator",
26+ "description": "You can specify a shell command which will be used to generate the environment, in which the extension will be executes. This is useful if you want to utilize `nix-shell`, `direnv` or any similar command.\n\nExpects the command to have the same stdout as the `env` command.",
27+ "type": "enum",
28+ "allowsCustom": true,
29+ "values": ["nix-shell --run env", "direnv exec env"]
30+ },
23 {
31 {
24 "type": "section",
32 "type": "section",
25 "title": "Haskell Language Server settings",
33 "title": "Haskell Language Server settings",