This commit is contained in:
iko 2022-02-19 16:31:53 +03:00
commit e9d579a1c1
Signed by untrusted user: iko
GPG Key ID: 82C257048D1026F2
11 changed files with 1647 additions and 0 deletions

View File

@ -0,0 +1 @@
.DS_Store

View File

@ -0,0 +1 @@
{"activeJourney":"2022/02/18 07:57","journeys":[{"identifier":"1645171063348_43","name":"2022/02/18 07:57","path":false,"line":false,"updatedAt":1645172096799,"children":[{"identifier":"1645171063356_35","name":"Syntaxes/Haskell.xml","path":"Syntaxes/Haskell.xml","line":false,"updatedAt":1645172096799,"comment":false,"children":[{"identifier":"1645171063356_45","name":"<collection name=\"pattern-match\">","path":"Syntaxes/Haskell.xml","line":101,"updatedAt":1645172096799,"comment":false,"children":[]}]}]}]}

View File

@ -0,0 +1,3 @@
## Version 1.0
Initial release

View File

@ -0,0 +1,72 @@
<!--
👋 Hello! As Nova users browse the extensions library, a good README can help them understand what your extension does, how it works, and what setup or configuration it may require.
Not every extension will need every item described below. Use your best judgement when deciding which parts to keep to provide the best experience for your new users.
💡 Quick Tip! As you edit this README template, you can preview your changes by selecting **Extensions → Activate Project as Extension**, opening the Extension Library, and selecting "Haskell Language Server" in the sidebar.
Let's get started!
-->
<!--
🎈 Include a brief description of the features your extension provides. For example:
-->
**Haskell Language Server** provides deep integration with **An Important Language**, including the most important feature, something that's really helpful, and _a little-known secret!_
<!--
🎈 It can also be helpful to include a screenshot or GIF showing your extension in action:
-->
![](https://nova.app/images/en/dark/editor.png)
## Requirements
<!--
🎈 If your extension depends on external processes or tools that users will need to have, it's helpful to list those and provide links to their installers:
-->
Haskell Language Server requires some additional tools to be installed on your Mac:
- [Node.js 8.2.0](https://nodejs.org) and NPM 5.2.0 or newer
<!--
✨ Providing tips, tricks, or other guides for installing or configuring external dependencies can go a long way toward helping your users have a good setup experience:
-->
> To install the current stable version of Node, click the "Recommended for Most Users" button to begin the download. When that completes, double-click the **.pkg** installer to begin installation.
## Usage
<!--
🎈 If your extension provides features that are invoked manually, consider describing those options for users:
-->
To run Haskell Language Server:
- Select the **Editor → Haskell Language Server** menu item; or
- Open the command palette and type `Haskell Language Server`
<!--
🎈 Alternatively, if your extension runs automatically (as in the case of a validator), consider showing users what they can expect to see:
-->
Haskell Language Server runs any time you open a local project, automatically lints all open files, then reports errors and warnings in Nova's **Issues** sidebar and the editor gutter:
![](https://nova.app/images/en/dark/tools/sidebars.png)
### Configuration
<!--
🎈 If your extension offers global- or workspace-scoped preferences, consider pointing users toward those settings. For example:
-->
To configure global preferences, open **Extensions → Extension Library...** then select Haskell Language Server's **Preferences** tab.
You can also configure preferences on a per-project basis in **Project → Project Settings...**
<!--
👋 That's it! Happy developing!
P.S. If you'd like, you can remove these comments before submitting your extension 😉
-->

View File

@ -0,0 +1,61 @@
exports.getHLSConfig = () => readAllConfigs(HLSKeys);
// X must be an object. It is modified in place and returned.
// The second is also an object and is merged into the left one.
function merge(x, y) {
for (key in y) {
if (typeof y[key] == "object" && x[key] && typeof x[key] == "object") {
x[key] = merge(x[key], y[key]);
} else {
x[key] = y[key];
}
}
return x;
}
function objectifyString(s, v) {
let previous = v;
for (key of s.split(".").reverse()) {
const next = {};
next[key] = previous;
previous = next;
}
return previous;
}
function readOneConfig(key) {
return objectifyString(key, nova.workspace.config.get(key));
}
function readAllConfigs(keys) {
return keys.map(readOneConfig).reduce(merge, {});
}
const HLSKeys = [
"haskell.formattingProvider",
"haskell.checkProject",
"haskell.maxCompletions",
"haskell.plugin.importLens.codeActionsOn",
"haskell.plugin.importLens.codeLensOn",
"haskell.plugin.hlint.codeActionsOn",
"haskell.plugin.hlint.diagnosticsOn",
"haskell.plugin.hlint.config.flags",
"haskell.plugin.eval.globalOn",
"haskell.plugin.moduleName.globalOn",
"haskell.plugin.splice.globalOn",
"haskell.plugin.haddockComments.globalOn",
"haskell.plugin.class.globalOn",
"haskell.plugin.retrie.globalOn",
"haskell.plugin.tactics.globalOn",
"haskell.plugin.tactics.config.auto_gas",
"haskell.plugin.tactics.config.hole_severity",
"haskell.plugin.tactics.config.max_use_ctor_actions",
"haskell.plugin.tactics.config.timeout_duration",
"haskell.plugin.tactics.config.proofstate_styling",
"haskell.plugin.pragmas.codeActionsOn",
"haskell.plugin.pragmas.completionOn",
"haskell.plugin.ghcide-completions.config.autoExtendOn",
"haskell.plugin.ghcide-completions.config.snippetsOn",
"haskell.plugin.ghcide-type-lenses.globalOn",
"haskell.plugin.ghcide-type-lenses.config.mode",
"haskell.plugin.refineImports.globalOn",
];

View File

@ -0,0 +1,158 @@
const {
sendNotification,
sendPermanentNotification,
} = require("notifications");
const {
readStream,
getStorage,
} = require("utils");
exports.getHLS = getHLS;
let assets = null;
async function getHLSAssets() {
if (!assets) {
const response = await fetch(
"https://api.github.com/repos/haskell/haskell-language-server/releases",
);
if (!response.ok) {
console.error(response.statusText);
return null;
}
const respObj = await response.json();
const pairs = respObj[0].assets.flatMap((asset) => {
const match = asset.name.match("haskell-language-server-macOS-(.*)\.gz");
if (match) {
return [
[match[1], asset.browser_download_url],
];
} else if (asset.name === "haskell-language-server-wrapper-macOS.gz") {
return [
["wrapper", asset.browser_download_url],
];
} else {
return [];
}
});
assets = Object.fromEntries(pairs);
}
return assets;
}
async function downloadTool(name) {
notifyToolDownloading(name);
const storagePath = getStorageForTool(name);
console.log("Downloading " + storagePath);
const urls = await getHLSAssets();
const response = await fetch(urls[name]);
if (!response.ok) {
console.error(response.statusText);
return null;
}
const p = new Promise((resolve, _reject) => {
const gzip = new Process("/usr/bin/gzip", {
args: ["-d"],
});
response.body.pipeTo(gzip.stdin);
const file = nova.fs.open(storagePath, "x+b");
const outReader = gzip.stdout.getReader();
outReader.read().then(function processChunk({
done,
value,
}) {
if (done) {
file.close;
console.log("Downloaded " + storagePath);
const chmod = new Process("/bin/chmod", {
args: ["+x", storagePath],
});
chmod.onDidExit((exitCode) => {
console.log(exitCode);
resolve(storagePath);
});
chmod.start();
} else {
file.write(value);
outReader.read().then(processChunk);
}
});
console.log("unzipping");
gzip.start();
});
return await p;
}
async function getTool(name) {
const path = getStorageForTool(name);
if (nova.fs.access(path, nova.fs.X_OK)) {
console.log("Exists: " + path);
return path;
} else {
console.log("Doesn't exists: " + path);
nova.fs.remove(path);
return await downloadTool(name);
}
}
async function getGHCVersion() {
const wrapper = await getTool("wrapper");
sendNotification("Woking out the GHC version of the project", "");
const process = new Process(wrapper, {
cwd: nova.workspace.path,
args: ["--project-ghc-version"],
});
const promise = new Promise((resolve, _reject) => {
process.onDidExit((exitCode) => {
if (exitCode == 0) {
readStream(process.stdout).then((x) => {
const version = x.trim();
sendNotification("Detected GHC " + version, "");
resolve(version);
});
} else {
readStream(process.stderr).then((x) => {
const rRes = x.match(/Cradle requires (.+) but couldn't find it/);
if (rRes) {
let tool;
switch (rRes[1]) {
case "stack":
tool = "Stack";
break;
case "cabal":
tool = "Cabal";
break;
case "ghc":
tool = "GHC";
break;
default:
tool = rRes[1];
break;
}
sendPermanentNotification(
tool + " not installed!",
"Please install " + tool +
" and restart Haskell Language Server.",
);
} else {
sendPermanentNotification("Couldn't detect GHC version", x);
}
});
}
});
});
process.start();
return promise;
}
async function getHLS() {
const version = await getGHCVersion();
return await getTool(version);
}
function getStorageForTool(name) {
return getStorage() + "/" + getToolName(name);
}
function getToolName(name) {
return "haskell-language-server-" + name;
}
function notifyToolDownloading(name) {
sendNotification("Downloading tool", "Downloading " + getToolName(name));
}

View File

@ -0,0 +1,143 @@
const {
sendNotification,
sendNotificationWithAction,
} = require("notifications");
const {
getHLS,
} = require("download");
const {
getHLSConfig,
} = require("configuration");
const {
getFileName,
} = require("utils");
exports.activate = function () {
nova.commands.register("stop", (_workspace) => {
stopServer();
});
nova.commands.register("restart", (_workspace) => {
startServer();
});
nova.commands.register("clear-cache", (_workspace) => {
nova.fs.rmdir(nova.extension.globalStoragePath);
startServer();
});
nova.commands.register("format", formatFile);
// startServer();
};
exports.deactivate = function () {
// Clean up state before the extension is deactivated
if (lspClient) {
lspClient.stop();
lspClient = null;
}
};
function formatFile(editor) {
// If there is no URI, there's not much we can do ;(
if (editor.document.uri) {
const name = getFileName(editor.document.uri);
let valid = true;
const removeNotification = sendNotificationWithAction(
"Formatting " + name,
"Waiting for Haskell Language Server to format " + name,
"Cancel",
() => {
valid = false;
},
);
editor.onDidChange(() => {
removeNotification();
valid = false;
});
const req = {
textDocument: {
uri: editor.document.uri,
},
options: {
tabSize: editor.tabLength,
insertSpaces: true,
},
};
lspClient.sendRequest("textDocument/formatting", req).then((edits) => {
editor.edit((edit) => {
if (valid) {
const range = new Range(0, editor.document.length);
const documentText = editor.document.getTextInRange(range);
const newText = applyAllLSPEdits(edits, documentText);
if (valid) {
edit.replace(range, newText);
}
}
removeNotification();
});
});
}
}
function applyAllLSPEdits(edits, text) {
let i = 0;
let line = 0;
let out = "";
function goToLine(l) {
while (line < l) {
if (text.slice(i, i + 2) === "\r\n") {
line += 1;
i += 1;
} else if (text[i] === "\n" || text[i] === "\r") {
line += 1;
}
i += 1;
}
}
let edit;
for (edit of edits) {
const prev = i;
goToLine(edit.range.start.line);
i += edit.range.start.character;
out += text.slice(prev, i);
goToLine(edit.range.end.line);
i += edit.range.end.character;
out += edit.newText;
console.log(out);
}
out += text.slice(i);
return out;
}
let lspClient = null;
async function startServer() {
stopServer();
const path = await getServerPath();
const serverOptions = {
path: path,
args: ["lsp", "--cwd", nova.workspace.path],
};
const clientOptions = {
syntaxes: ["haskell"],
initializationOptions: getHLSConfig(),
};
lspClient = new LanguageClient(
"haskell-language-server",
nova.extension.name,
serverOptions,
clientOptions,
);
sendNotification("Starting Haskell Language Server", "");
lspClient.start();
}
function stopServer() {
if (lspClient) {
sendNotification("Stopping Haskell Language Server", "");
lspClient.stop();
lspClient = null;
}
}
function getServerPath() {
return getHLS();
}
// function getLogPath() {
// return getStorage() + "/haskell-language-server.log";
// }

View File

@ -0,0 +1,36 @@
function sendNotification(title, body) {
const request = new NotificationRequest();
request.title = title;
request.body = body;
nova.notifications.add(request);
}
function sendPermanentNotification(title, body) {
const request = new NotificationRequest();
request.title = title;
request.body = body;
request.actions = ["OK"];
nova.notifications.add(request);
}
function sendNotificationWithAction(title, body, actionName, action) {
const now = Date.now().toString();
const request = new NotificationRequest(now);
request.title = title;
request.body = body;
request.actions = [actionName];
nova.notifications.add(request).then((x) => {
if (x) {
action();
}
});
return () => {
nova.notifications.cancel(now);
};
}
module.exports = {
sendNotification,
sendPermanentNotification,
sendNotificationWithAction,
};

View File

@ -0,0 +1,51 @@
function readStream(readableStream) {
return new Promise((resolve, _reject) => {
const reader = readableStream.getReader();
let out = "";
reader.read().then(function processChunk({
done,
value,
}) {
if (done) {
resolve(out);
} else {
out += arrayBufferToString(value);
reader.read().then(processChunk);
}
});
});
}
function arrayBufferToString(buffer) {
const arr = new Uint8Array(buffer);
const str = String.fromCharCode.apply(String, arr);
if (/[\u0080-\uffff]/.test(str)) {
throw new Error("this string seems to contain (still encoded) multibytes");
}
return str;
}
function getStorage() {
try {
nova.fs.mkdir(nova.extension.globalStoragePath);
} catch (e) {
console.error(e);
}
return nova.extension.globalStoragePath;
}
function getFileName(path) {
return path.replace(/^.*[\\\/]/, "");
}
function trace(x) {
console.log(JSON.stringify(x));
return x;
}
function traceRaw(x) {
console.log(x);
return x;
}
module.exports = { readStream, getStorage, getFileName, trace, traceRaw };

View File

@ -0,0 +1,821 @@
<?xml version="1.0" encoding="UTF-8"?>
<syntax name="haskell">
<meta>
<name>Haskell</name>
<type>compiled</type>
<preferred-file-extension>hs</preferred-file-extension>
</meta>
<detectors>
<extension priority="1.0">hs</extension>
</detectors>
<comments>
<single>
<expression>--</expression>
</single>
<multiline>
<starts-with>
<expression>{-</expression>
</starts-with>
<ends-with>
<expression>-}</expression>
</ends-with>
</multiline>
</comments>
<brackets>
<pair open="{" close="}" />
<pair open="[" close="]" />
<pair open="(" close=")" />
</brackets>
<surrounding-pairs>
<pair open="[" close="]" />
<pair open="(" close=")" />
<pair open="&apos;" close="&apos;" />
<pair open="&quot;" close="&quot;" />
<pair open="`" close="`" />
</surrounding-pairs>
<indentation>
<increase>
<expression>
(case|class|do|let|where).*
</expression>
</increase>
<decrease>
<expression>^\\s*(end|done|with|in|else)\\b|^\\s*;;</expression>
</decrease>
</indentation>
<scopes>
<include syntax="self" collection="comments" />
<include syntax="self" collection="module" />
<include syntax="self" collection="import" />
<include syntax="self" collection="type-family-definition" />
<include syntax="self" collection="pattern-definition" />
<include syntax="self" collection="type-definition" />
<!-- <include syntax="self" collection="type-annotation" /> -->
<include syntax="self" collection="data-definition" />
<include syntax="self" collection="GADT-definition" />
<include syntax="self" collection="instance" />
<include syntax="self" collection="typeclass" />
<include syntax="self" collection="definition" />
</scopes>
<template-scopes>
<include syntax="self" collection="Quote" />
<include syntax="self" collection="comments" />
</template-scopes>
<collections>
<collection name="operators">
<scope name="keyword.modifier">
<expression>(!|#|\$|%|&amp;|⋆|\+|\.|/|&lt;|=|&gt;|\?|@|\\|\^|\||-|~|:)+</expression>
</scope>
</collection>
<collection name="expressions">
<include syntax="self" collection="do-bind" />
<include syntax="self" collection="literal" />
<include syntax="self" collection="operators" />
<include syntax="self" collection="list" />
<include syntax="self" collection="lambda-case" />
<include syntax="self" collection="lambda-head" />
<include syntax="self" collection="type-applications" />
<include syntax="self" collection="case-expression" />
<include syntax="self" collection="qvarid" />
<include syntax="self" collection="bind-arrow" />
<include syntax="self" collection="tuple" />
<!-- Needed for do binds -->
<!-- <include syntax="self" collection="pattern-match" /> -->
</collection>
<collection name="pattern-match">
<include syntax="self" collection="literal" />
<include syntax="self" collection="view-pattern" />
<include syntax="self" collection="tuple-match" />
<!-- <include syntax="self" collection="at-pattern" /> -->
<include syntax="self" collection="varid" />
<include syntax="self" collection="qconid" />
<include syntax="self" collection="record-match" />
<include syntax="self" collection="list-pattern" />
</collection>
<collection name="type-applications">
<scope name="type-applications">
<starts-with>
<expression>(\s@)\(</expression>
<capture number="1" name="haskell.keyword.operator.typeapplication" />
</starts-with>
<ends-with>
<expression>)</expression>
</ends-with>
<subscopes>
<include syntax="self" collection="type" />
</subscopes>
</scope>
<scope name="type-applications-single">
<expression>(@)(\w+)</expression>
<capture number="1" name="haskell.keyword.operator.typeapplication" />
<capture number="2" name="haskell.identifier.type" />
</scope>
</collection>
<collection name="Quote">
<scope name="haskell.quasiquote.string-template">
<starts-with>
<expression>\[((?:\w|')+)\|</expression>
<capture number="1" name="haskell.identifier.constant" />
</starts-with>
<ends-with>
<expression>\|\]</expression>
</ends-with>
<subscopes>
<scope name="haskell.quasiquote.string-template.value">
<expression>.+?</expression>
</scope>
</subscopes>
</scope>
</collection>
<collection name="record-match">
<include syntax="self" collection="qconid" />
<include syntax="self" collection="record-fields-match" />
<include syntax="self" collection="record-fields-match-all" />
</collection>
<collection name="record-fields-match">
<scope name="record-fields-match">
<symbol type="expression" />
<starts-with>
<expression>(\{)</expression>
<capture number="1" name="haskell.pattern.record.bracket" />
</starts-with>
<ends-with>
<expression>(\})</expression>
<capture number="1" name="haskell.pattern.record.bracket" />
</ends-with>
<subscopes>
<include syntax="self" collection="pattern-match" />
<include syntax="self" collection="varid" />
<scope name="record-sep">
<expression>=</expression>
</scope>
<scope name="record-sep">
<expression>,</expression>
</scope>
</subscopes>
</scope>
</collection>
<collection name="record-fields-match-wildcard">
<scope name="record-fields-match-all">
<starts-with>
<expression>(\{)</expression>
<capture number="1" name="haskell.pattern.record.bracket" />
</starts-with>
<ends-with>
<expression>(\})</expression>
<capture number="1" name="haskell.pattern.record.bracket" />
</ends-with>
<subscopes>
<scope name="haskell.pattern.record.wildcard">
<expression>\.\.</expression>
</scope>
</subscopes>
</scope>
</collection>
<collection name="bind-arrow">
<scope name="haskell.operator.bind-arrow">
<expression>&lt;-</expression>
</scope>
</collection>
<collection name="list">
<symbol type="expression" />
<scope name="haskell.list">
<starts-with>
<expression>(\[)</expression>
<capture number="1" name="haskell.list.bracket" />
</starts-with>
<ends-with>
<expression>(\])</expression>
<capture number="1" name="haskell.list.bracket" />
</ends-with>
<subscopes>
<include syntax="self" collection="expressions" />
</subscopes>
</scope>
</collection>
<collection name="list-pattern">
<symbol type="expression" />
<scope name="haskell.pattern.list">
<starts-with>
<expression>(\[)</expression>
<capture number="1" name="haskell.pattern.list.bracket" />
</starts-with>
<ends-with>
<expression>(\])</expression>
<capture number="1" name="haskell.pattern.list.bracket" />
</ends-with>
<subscopes>
<include syntax="self" collection="pattern-match" />
</subscopes>
</scope>
</collection>
<collection name="view-pattern">
<scope name="haskell.pattern.view">
<symbol type="expression" />
<starts-with>
<expression>(\()</expression>
<capture number="1" name="haskell.pattern.view.bracket" />
</starts-with>
<ends-with>
<expression>(\))</expression>
<capture number="1" name="haskell.pattern.view.bracket" />
</ends-with>
<subscopes anchored="true">
<include syntax="self" collection="expressions" />
<scope name="haskell.pattern.view.operator.arrow">
<expression>-&gt;</expression>
</scope>
<include syntax="self" collection="pattern-match" />
</subscopes>
</scope>
</collection>
<collection name="tuple-match">
<symbol type="expression" />
<scope name="haskell.pattern.tuple">
<starts-with>
<expression>(\()</expression>
<capture number="1" name="haskell.pattern.tuple.bracket" />
</starts-with>
<ends-with>
<expression>(\))</expression>
<capture number="1" name="haskell.pattern.tuple.bracket" />
</ends-with>
<subscopes>
<include syntax="self" collection="pattern-match" />
</subscopes>
</scope>
</collection>
<collection name="case-expression">
<scope name="haskell.case">
<starts-with>
<expression>(case)</expression>
<capture number="1" name="haskell.case.keyword.condition" />
</starts-with>
<ends-with>
<expression>(of)</expression>
<capture number="1" name="haskell.case.keyword.condition" />
</ends-with>
<subscopes>
<include syntax="self" collection="expressions" />
</subscopes>
</scope>
</collection>
<collection name="tuple">
<symbol type="expression" />
<scope name="haskell.tuple">
<starts-with>
<expression>(\()</expression>
<capture number="1" name="haskell.tuple.bracket" />
</starts-with>
<ends-with>
<expression>(\))</expression>
<capture number="1" name="haskell.tuple.bracket" />
</ends-with>
<subscopes>
<include syntax="self" collection="expressions" />
</subscopes>
</scope>
</collection>
<collection name="typeclass">
<symbol type="interface" />
<scope name="haskell.definition.class">
<starts-with>
<expression>^(\h*)(class)($|\h)</expression>
<capture number="2" name="haskell.keyword.construct.class" />
</starts-with>
<ends-with>
<template>^(?!\s+\1\S)(?!\s*$)</template>
</ends-with>
<subscopes>
<scope name="haskell.keyword.construct.where">
<expression>where</expression>
</scope>
<include syntax="self" collection="type" />
<include syntax="self" collection="type-definition" />
<!-- <include syntax="self" collection="type-annotation" /> -->
<include syntax="self" collection="definition" />
</subscopes>
</scope>
</collection>
<collection name="instance">
<scope name="haskell.instance">
<symbol type="category" />
<starts-with>
<expression>^(\h*)(instance)($|\h)</expression>
<capture number="2" name="haskell.keyword.construct.instance" />
</starts-with>
<ends-with>
<template>^(?!\s+\1\S)(?!\s*$)</template>
</ends-with>
<subscopes>
<scope name="haskell.keyword.construct.where">
<expression>where</expression>
</scope>
<include syntax="self" collection="type" />
<include syntax="self" collection="type-definition" />
<include syntax="self" collection="type-annotation" />
<include syntax="self" collection="definition" />
</subscopes>
</scope>
</collection>
<collection name="type-family-definition">
<scope name="haskell.definition.type.type-family">
<symbol type="type" />
<starts-with>
<expression>^(\h*)(type\h+family)\h+`?(\w(\w|')*)`?($|\h)</expression>
<capture number="2" name="haskell.keyword.construct.type-family" />
</starts-with>
<ends-with>
<template>^(?!\s+\1\S)(?!\s*$)</template>
</ends-with>
<subscopes>
<scope name="haskell.keyword.construct.where">
<expression>where</expression>
</scope>
<include syntax="self" collection="type" />
</subscopes>
</scope>
</collection>
<collection name="definition">
<scope name="haskell.definition.type.function">
<symbol type="function">
<display-name>
<component selector="haskell.definition.type.function" />
</display-name>
</symbol>
<starts-with>
<expression>^(?=(\h*)(default\h+)?`?(\w(\w|')*)`?($|\h))</expression>
<capture number="2" name="haskell.keyword.construct.default" />
<capture number="3" name="haskell.identifier.function" />
</starts-with>
<ends-with>
<template>^(?!\1\3\s)(?!\s+\1\S)(?!\s*$)</template>
</ends-with>
<subscopes>
<include syntax="self" collection="type-annotation" />
<include syntax="self" collection="implementation" />
</subscopes>
</scope>
</collection>
<collection name="do-bind">
<scope name="do-bind">
<starts-with>
<expression>^(?=(\h*).+&lt;-($|\h))</expression>
</starts-with>
<ends-with>
<template>^(?!\0)(?!\s+\1\S)(?!\s*$)</template>
</ends-with>
<subscopes anchored="true">
<include syntax="self" collection="pattern-match" />
<scope name="bind-arrow">
<expression>&lt;-</expression>
</scope>
<include syntax="self" collection="expressions" repeat="true" />
</subscopes>
</scope>
</collection>
<collection name="implementation">
<scope name="haskell.definition.type.function">
<local scope="within-construct" />
<starts-with>
<expression>^(\h*)(default\h+)?`?(\w(\w|')*)`?($|\h)</expression>
<capture number="2" name="haskell.keyword.construct.default" />
<capture number="3" name="haskell.identifier.function" />
</starts-with>
<ends-with>
<template>^(?!\1\3\s)(?!\s+\1\S)(?!\s*$)</template>
</ends-with>
<subscopes>
<include syntax="self" collection="where-clause" />
<include syntax="self" collection="expressions" />
</subscopes>
</scope>
</collection>
<collection name="type-annotation">
<scope name="haskell.definition.type.function">
<starts-with>
<expression>^(\h*)`?(\w(\w|')*)`?\h*::($|\h)</expression>
<capture number="2" name="haskell.identifier.function" />
</starts-with>
<ends-with>
<template>^(?!\s+\1\S)(?!\s*$)</template>
</ends-with>
<subscopes>
<include syntax="self" collection="type" />
</subscopes>
</scope>
</collection>
<collection name="type-definition">
<scope name="haskell.definition.type">
<starts-with>
<expression>^(\h*)(type)\h+(\w(\w|')*)</expression>
<capture number="2" name="haskell.keyword.construct.type" />
<capture number="3" name="haskell.identifier.type" />
</starts-with>
<ends-with>
<template>^(?!\s+\1\S)(?!\s*$)</template>
</ends-with>
<subscopes>
<include syntax="self" collection="type" />
</subscopes>
</scope>
</collection>
<collection name="GADT-definition">
<scope name="haskell.definition.struct.GADT">
<starts-with>
<expression>^(\h*)(data)\h+(\w(\w|')*)</expression>
<capture number="2" name="haskell.keyword.construct.data.GADT" />
</starts-with>
<ends-with>
<template>^(?!\s+\1\S)(?!\s*$)</template>
</ends-with>
<subscopes>
<include syntax="self" collection="gadt-constructor" />
<include syntax="self" collection="type" />
<include syntax="self" collection="record-definition" />
<include syntax="self" collection="record-definition-field-name" />
</subscopes>
</scope>
</collection>
<collection name="gadt-constructor">
<scope name="identifier.type.struct.GADT.constructor">
<expression>^\h+[A-Z](\w|')*</expression>
</scope>
</collection>
<collection name="data-definition">
<scope name="haskell.definition.struct.data">
<starts-with>
<expression>^(\h*)(data|newtype)\h+(\w(\w|')*)</expression>
<capture number="2" name="haskell.keyword.construct.data.newtype" />
<capture number="3" name="haskell.identifier.type" />
</starts-with>
<ends-with>
<template>^(?!\s+\1\S)(?!\s*$)</template>
</ends-with>
<subscopes>
<include syntax="self" collection="record-definition" />
<include syntax="self" collection="data-constructor" />
<include syntax="self" collection="type" />
</subscopes>
</scope>
</collection>
<collection name="data-constructor">
<scope name="haskell.identifier.type.struct.data.constructor">
<expression>=\h+[A-Z](\w|')*</expression>
</scope>
<scope name="haskell.identifier.type.struct.data.constructor">
<expression>\|\h+[A-Z](\w|')*</expression>
</scope>
</collection>
<collection name="record-definition">
<scope name="haskell.record">
<starts-with>
<expression>(\{)</expression>
<capture number="1" name="haskell.record.bracket" />
</starts-with>
<ends-with>
<expression>(\})</expression>
<capture number="1" name="haskell.record.bracket" />
</ends-with>
<subscopes>
<include syntax="self" collection="record-definition-field-name" />
<include syntax="self" collection="type" />
</subscopes>
</scope>
</collection>
<collection name="record-definition-field-name">
<scope name="haskell.record.definition.property">
<expression>\w(\w|')*\h*::</expression>
</scope>
</collection>
<collection name="type">
<include syntax="self" collection="type-list" />
<scope name="haskell.identifier.type">
<expression>([A-Z](\w|')*\.)*[A-Z](\w|')*</expression>
</scope>
<scope name="haskell.keyword.construct.forall">
<expression>forall</expression>
</scope>
<scope name="haskell.identifier.type.variable">
<expression>\w(\w|')*</expression>
</scope>
<scope name="haskell.keyword.construct.function-arrow">
<expression>-&gt;</expression>
</scope>
<scope name="haskell.keyword.construct.fat-arrow">
<expression>=&gt;</expression>
</scope>
</collection>
<collection name="pattern-definition">
<scope name="haskell.pattern">
<starts-with>
<expression>^(\h*)(pattern)\h+(\w(\w|')*)</expression>
<capture number="2" name="haskell.keyword.construct.pattern" />
<capture number="3" name="haskell.identifier.decorator.pattern" />
</starts-with>
<ends-with>
<template>^(?!\s+\1\S)(?!\s*$)</template>
</ends-with>
<subscopes>
<scope name="haskell.keyword.construct.where">
<expression>where</expression>
</scope>
<include syntax="self" collection="expressions" />
</subscopes>
</scope>
</collection>
<collection name="type-list">
<scope name="haskell.type-list">
<starts-with>
<expression>('\[)</expression>
<capture number="1" name="haskell.type-list.bracket" />
</starts-with>
<ends-with>
<expression>(\])</expression>
<capture number="1" name="haskell.type-list.bracket" />
</ends-with>
<subscopes>
<include syntax="self" collection="type" />
</subscopes>
</scope>
</collection>
<collection name="lambda-head">
<scope name="haskell.lambda">
<starts-with>
<expression>(\\)</expression>
<capture number="1" name="haskell.keyword.construct.lambda" />
</starts-with>
<ends-with>
<expression>(-&gt;)</expression>
<capture number="1" name="haskell.keyword.construct.lambda" />
</ends-with>
<subscopes>
<include syntax="self" collection="pattern-match" />
</subscopes>
</scope>
</collection>
<collection name="lambda-case">
<scope name="haskell.keyword.condition.lambda-case">
<expression>\\case</expression>
</scope>
</collection>
<collection name="where-clause">
<scope name="haskell.construct.where">
<local scope="within-parent" />
<starts-with>
<expression>^(\h*)(where)</expression>
<capture number="2" name="haskell.construct.keyword.where" />
</starts-with>
<ends-with>
<template>^(?!\1\s*\S)(?!s*$)</template>
</ends-with>
<subscopes>
<include syntax="self" collection="definition" />
</subscopes>
</scope>
</collection>
<collection name="qvarid">
<scope name="haskell.identifier.function">
<expression>([A-Z](\w|')*\.)*\w(\w|')*</expression>
</scope>
</collection>
<collection name="varid">
<scope name="haskell.identifier.variable">
<expression>\w(\w|')*</expression>
</scope>
</collection>
<collection name="qconid">
<scope name="haskell.identifier.type.struct.constructor">
<expression>([A-Z](\w|')*\.)*[A-Z](\w|')*</expression>
</scope>
</collection>
<collection name="qconsym">
<scope name="haskell.identifier.decorator.pattern">
<expression>([A-Z](\w|')*\.):(\w|')*</expression>
</scope>
</collection>
<collection name="literal">
<include syntax="self" collection="number" />
<include syntax="self" collection="strings" />
</collection>
<collection name="strings">
<scope name="haskell.string.double-quoted">
<starts-with>
<expression>"</expression>
</starts-with>
<ends-with>
<expression>"</expression>
</ends-with>
<subscopes>
<scope name="haskell.string.escape-sequences">
<expression>\\.</expression>
</scope>
</subscopes>
</scope>
<scope name="haskell.string.single-quoted">
<expression>'([^'\\]|\\.)'</expression>
</scope>
</collection>
<collection name="number">
<scope name="haskell.value.number">
<expression>(0x|0o|0O|0X)?\d+(\d|_)*(\.[\d|_]+)?((e|E)(\+|-)?\d+)?</expression>
</scope>
</collection>
<collection name="module">
<scope name="haskell.definition.module.package">
<starts-with>
<expression>^(module)\s+(\w|\.)+</expression>
<capture number="1" name="haskell.construct.keyword.module" />
<capture number="2" name="haskell.definition.module.package" />
</starts-with>
<ends-with>
<expression>(where)</expression>
<capture number="1" name="haskell.construct.keyword.module" />
</ends-with>
<subscopes export-symbols="true">
<scope name="identifier.constant">
<expression>(\w|')+</expression>
</scope>
</subscopes>
</scope>
</collection>
<collection name="import">
<scope name="haskell.construct.keyword.import">
<expression>^import\s</expression>
</scope>
<scope name="haskell.construct.keyword.qualified">
<expression>qualified</expression>
</scope>
<scope name="haskell.construct.keyword.as">
<expression>as</expression>
</scope>
<scope name="haskell.identifier.module.package">
<expression>([A-Z](\w|')*\.)*[A-Z](\w|')*</expression>
</scope>
<include syntax="self" collection="import-list" />
</collection>
<collection name="import-list">
<scope name="import-list">
<starts-with>
<expression>(\()</expression>
<capture number="1" name="haskell.import-list.bracket" />
</starts-with>
<ends-with>
<expression>(\))</expression>
<capture number="1" name="haskell.import-list.bracket" />
</ends-with>
<subscopes>
<scope name="identifier.decorator">
<expression>([A-Z](\w|')*\.)*\w(\w|')*</expression>
</scope>
</subscopes>
</scope>
</collection>
<!-- <collection name="special">
<scope>
<expression>(;|,)</expression>
</scope>
</collection> -->
<collection name="reservedop">
<scope name="keyword.operator">
<expression>(\.\.|:|::|=|\\|\||&lt;-|-&gt;|@|~|=>)</expression>
</scope>
</collection>
<collection name="keywords">
<scope name="haskell.keyword.modifier">
<symbol type="keyword" />
<strings>
<string>do</string>
<string>in</string>
<string>infix</string>
<string>infixl</string>
<string>infixr</string>
<string>where</string>
<string>let</string>
</strings>
</scope>
<scope name="haskell.keyword.condition">
<symbol type="keyword" />
<strings>
<string>of</string>
<string>case</string>
<string>else</string>
<string>if</string>
<string>then</string>
</strings>
</scope>
<scope name="haskell.keyword.keyword.construct">
<symbol type="keyword" />
<strings>
<string>instance</string>
<string>newtype</string>
<string>module</string>
<string>type</string>
<string>class</string>
<string>data</string>
<string>default</string>
<string>deriving</string>
<string>foreign</string>
</strings>
</scope>
<scope name="haskell.keyword.self">
<symbol type="keyword" />
<strings>
<string>undefined</string>
<string>_</string>
</strings>
</scope>
</collection>
<collection name="comments">
<scope name="haskell.comment.single" spell-check="true">
<starts-with>
<expression>--</expression>
</starts-with>
<ends-with>
<expression>$</expression>
</ends-with>
</scope>
<scope name="haskell.comment.block" spell-check="true">
<starts-with>
<expression>{-</expression>
</starts-with>
<ends-with>
<expression>-}</expression>
</ends-with>
</scope>
</collection>
</collections>
</syntax>

View File

@ -0,0 +1,300 @@
{
"identifier": "iko.soy.HaskellLanguageServer",
"name": "Haskell Language Server",
"organization": "iko",
"description": "Lorem ipsum, dolor sit amet.",
"version": "1.0",
"categories": ["languages", "formatters"],
"main": "main.js",
"entitlements": {
"process": true,
"filesystem": "readwrite",
"requests": true
},
"configWorkspace": [{
"key": "haskell-language-server-path",
"title": "Haskell Language Server path",
"description": "If you want to use a custom HLS build for this project, then specify the path to it here.",
"type": "path",
"placeholder": "/usr/local/bin/example"
}, {
"type": "section",
"title": "Haskell Language Server settings",
"children": [{
"key": "haskell.formattingProvider",
"title": "Code formatter",
"type": "enum",
"values": [
"brittany",
"floskell",
"fourmolu",
"ormolu",
"stylish-haskell",
"none"
],
"default": "ormolu",
"description": "The formatter to use when formatting a document or range. Ensure the plugin is enabled."
}, {
"key": "haskell.checkProject",
"title": "Check whole project",
"type": "boolean",
"default": false,
"description": "Whether to typecheck the entire project on load. It could drive to bad perfomance in large projects."
}, {
"key": "haskell.maxCompletions",
"type": "number",
"title": "Maximum number of completions",
"description": "Maximum number of completions sent to the editor.",
"default": 40
}, {
"type": "section",
"title": "Plugin settings",
"children": [{
"type": "section",
"title": "Import lens",
"description": "Not currently implemented in Nova",
"children": [{
"key": "haskell.plugin.importLens.codeActionsOn",
"title": "Enable code actions",
"type": "boolean",
"default": true,
"description": "Enables explicit imports code actions"
}, {
"key": "haskell.plugin.importLens.codeLensOn",
"title": "Enable code lens",
"type": "boolean",
"default": true,
"description": "Enables explicit imports code lenses"
}]
}, {
"title": "HLint",
"type": "section",
"children": [{
"key": "haskell.plugin.hlint.codeActionsOn",
"title": "Enable code actions",
"type": "boolean",
"default": true,
"description": "Enables hlint code actions (apply hints)"
}, {
"key": "haskell.plugin.hlint.diagnosticsOn",
"title": "Enable diagnostics",
"type": "boolean",
"default": true,
"description": "Enables hlint diagnostics"
}, {
"title": "Configuration",
"type": "section",
"children": [{
"key": "haskell.plugin.hlint.config.flags",
"title": "Flags",
"description": "Flags used by hlint",
"default": [],
"type": "stringArray"
}]
}]
}, {
"title": "Eval",
"type": "section",
"children": [{
"key": "haskell.plugin.eval.globalOn",
"title": "Enable",
"type": "boolean",
"default": true,
"description": "Enables eval plugin"
}]
}, {
"title": "Module name",
"type": "section",
"children": [{
"key": "haskell.plugin.moduleName.globalOn",
"title": "Enable",
"type": "boolean",
"default": true,
"description": "Enables module name plugin"
}]
}, {
"title": "Splice",
"type": "section",
"children": [{
"key": "haskell.plugin.splice.globalOn",
"title": "Enable",
"type": "boolean",
"default": true,
"description": "Enables splice plugin (expand template haskell definitions)"
}]
}, {
"title": "Haddock comments",
"type": "section",
"children": [{
"key": "haskell.plugin.haddockComments.globalOn",
"title": "Enable",
"type": "boolean",
"default": true,
"description": "Enables haddock comments plugin"
}]
}, {
"title": "Class",
"type": "section",
"children": [{
"key": "haskell.plugin.class.globalOn",
"title": "Enable",
"type": "boolean",
"default": true,
"description": "Enables type class plugin"
}]
}, {
"title": "Retrie",
"type": "section",
"children": [{
"key": "haskell.plugin.retrie.globalOn",
"title": "Enable",
"type": "boolean",
"default": true,
"description": "Enables retrie plugin"
}]
}, {
"title": "Wingman",
"type": "section",
"children": [{
"key": "haskell.plugin.tactics.globalOn",
"title": "Enable",
"type": "boolean",
"default": true,
"description": "Enables Wingman (tactics) plugin"
}, {
"title": "Configuration",
"type": "section",
"children": [{
"key": "haskell.plugin.tactics.config.auto_gas",
"title": "Gas",
"description": "The depth of the search tree when performing \"Attempt to fill hole\". Bigger values will be able to derive more solutions, but will take exponentially more time.",
"default": 4,
"type": "number"
}, {
"key": "haskell.plugin.tactics.config.hole_severity",
"title": "Hole severity",
"description": "The severity to use when showing hole diagnostics.",
"values": [
1,
2,
3,
4,
null
],
"default": null,
"type": "enum"
}, {
"key": "haskell.plugin.tactics.config.max_use_ctor_actions",
"title": "Max number of constructors",
"type": "number",
"default": 5,
"description": "Maximum number of `Use constructor <x>` code actions that can appear"
}, {
"key": "haskell.plugin.tactics.config.timeout_duration",
"title": "Timeout",
"description": "The timeout for Wingman actions, in seconds",
"default": 2,
"type": "number"
}, {
"key": "haskell.plugin.tactics.config.proofstate_styling",
"title": "Proof states",
"description": "Should Wingman emit styling markup when showing metaprogram proof states?",
"default": true,
"type": "boolean"
}]
}]
}, {
"title": "Pragmas",
"type": "section",
"children": [{
"key": "haskell.plugin.pragmas.codeActionsOn",
"title": "Enable code actions",
"type": "boolean",
"default": true,
"description": "Enables pragmas code actions"
}, {
"key": "haskell.plugin.pragmas.completionOn",
"title": "Enable completions",
"type": "boolean",
"default": true,
"description": "Enables pragmas completions"
}]
}, {
"title": "Completions",
"type": "section",
"children": [{
"title": "Configuration",
"type": "section",
"children": [{
"key": "haskell.plugin.ghcide-completions.config.autoExtendOn",
"title": "Extend with out-of-scope identifiers",
"description": "Extends the import list automatically when completing a out-of-scope identifier",
"default": true,
"type": "boolean"
}, {
"key": "haskell.plugin.ghcide-completions.config.snippetsOn",
"title": "Insert snippets",
"description": "Inserts snippets when using code completions",
"default": true,
"type": "boolean"
}]
}]
}, {
"description": "Not currently implemented in Nova",
"type": "section",
"children": [{
"key": "haskell.plugin.ghcide-type-lenses.globalOn",
"title": "Enable",
"type": "boolean",
"default": true,
"description": "Enables type lenses plugin"
}, {
"title": "Configuration",
"type": "section",
"children": [{
"key": "haskell.plugin.ghcide-type-lenses.config.mode",
"title": "Mode",
"type": "enum",
"default": true,
"description": "Control how type lenses are shown",
"values": ["always", "exported", "diagnostics"]
}]
}]
}, {
"title": "Refine imports",
"type": "section",
"children": [{
"key": "haskell.plugin.refineImports.globalOn",
"title": "Enable",
"type": "boolean",
"default": true,
"description": "Enables refine imports plugin"
}]
}]
}]
}],
"activationEvents": ["onLanguage:haskell", "onLanguage:hs"],
"commands": {
"editor": [{
"title": "Format file with Haskell Language Server",
"command": "format",
"when": "editorSyntax == 'haskell' && editorHasFocus",
"filters": {
"syntaxes": ["haskell"]
}
}],
"extensions": [{
"title": "Stop",
"paletteTitle": "Stop Haskell Language Server",
"command": "stop"
}, {
"title": "Restart",
"paletteTitle": "Restart Haskell Language Server",
"command": "restart"
}, {
"title": "Clear cached versions",
"paletteTitle": "Clear cached versions of Haskell Language Server",
"command": "clear-cache"
}]
}
}