git.iko.soy/rowtype-yoga/purslint
on main help sign in
30 files changed+405−196
since splitsnapshots
unifiedsplit
.github/ISSUE_TEMPLATE/bug_report.yml+53 LICENSE+21 README.md+79−75 bin/purelint-lsp.js−2 bin/purelint.js−2 bin/purslint-cli/spago.yaml+3−2 bin/purslint-cli/src/Main.purs+91−46 bin/purslint-lsp.js+2 bin/purslint-lsp/spago.yaml+2−2 bin/purslint-lsp/src/LSP.purs+18−18 bin/purslint-lsp/src/LSP/Stdio.js+1−1 bin/purslint.js+2 demo.gif lib/purslint/spago.yaml+1−1 lib/purslint/src/Purslint/Fix.purs+12−9 package.json+6−6 purelint-lsp−2 purslint-lsp+2 spago.lock+91−10 spago.yaml+3−2 test/Test/Main.purs+1−1 vscode-extension/out/extension.js+2−2 vscode-extension/package.json+1−1 vscode-extension/purslint.jpg vscode-extension/purslint.png vscode-extension/src/extension.ts+2−2 zed-extension/Cargo.lock+3−3 zed-extension/Cargo.toml+1−1 zed-extension/extension.toml+3−3 zed-extension/src/lib.rs+5−5
.github/ISSUE_TEMPLATE/bug_report.ymlnew file+53
@@ -0,0 +1,53 @@
1+name: Bug report
2+description: Report incorrect lint results with input and output.
3+title: "[Bug]: "
4+labels:
5+ - bug
6+body:
7+ - type: markdown
8+ attributes:
9+ value: |
10+ Thanks for taking the time to report a bug.
11+ If possible, include a golden file with input, actual output, and expected output.
12+ - type: textarea
13+ id: input
14+ attributes:
15+ label: Input
16+ description: |
17+ Paste the input here (or attach a golden file and mention the filename).
18+ Include any relevant configuration.
19+ placeholder: |
20+ ```purs
21+ -- Your input code here
22+ ```
23+ validations:
24+ required: true
25+ - type: textarea
26+ id: actual
27+ attributes:
28+ label: Actual output
29+ description: Paste the current output from purslint.
30+ placeholder: |
31+ ```
32+ -- Warnings, fixes, or logs
33+ ```
34+ validations:
35+ required: true
36+ - type: textarea
37+ id: expected
38+ attributes:
39+ label: Expected output
40+ description: Describe the expected output or behaviour.
41+ placeholder: |
42+ ```
43+ -- Expected warnings, fixes, or logs
44+ ```
45+ validations:
46+ required: true
47+ - type: textarea
48+ id: extra
49+ attributes:
50+ label: Additional context
51+ description: Links, screenshots, or anything else helpful.
52+ validations:
53+ required: false
LICENSEnew file+21
@@ -0,0 +1,21 @@
1+MIT License
2+
3+Copyright (c) 2026 Rowtype Yoga
4+
5+Permission is hereby granted, free of charge, to any person obtaining a copy
6+of this software and associated documentation files (the "Software"), to deal
7+in the Software without restriction, including without limitation the rights
8+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+copies of the Software, and to permit persons to whom the Software is
10+furnished to do so, subject to the following conditions:
11+
12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
14+
15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+SOFTWARE.
README.md+79−75
@@ -1,79 +1,58 @@
1−# PureLint
1+# Purslint
22
3−![Purslint logo](vscode-extension/purslint.jpg)
3+<p align="center">
4+ <img src="vscode-extension/purslint.png" alt="Purslint logo" width="180" />
5+</p>
46
5−A PureScript linter inspired by [hlint](https://github.com/ndmitchell/hlint), providing suggestions for cleaner, more idiomatic PureScript code.
7+<p align="center">
8+ <a href="https://github.com/user-attachments/assets/6d855014-18f5-4729-be28-e91ec2d19c8f">
9+ <img src="demo.gif" alt="Purslint demo" width="480" />
10+ </a>
11+</p>
612
7−## Features
13+A PureScript linter.
814
9−PureLint currently ships with **81 lint rules**, covering common PureScript refactors and simplifications.
15+Purslint is very new and may have bugs, so please use an editor with undo functionality. :D
1016
11−For a complete list, see `lib/purslint/src/Purslint/Rules/`.
17+## Install
1218
13−## Installation
19+### VS Code (Marketplace)
1420
15−### npm (recommended)
21+Install from the Visual Studio Marketplace:
22+[Purslint](https://marketplace.visualstudio.com/items?itemName=RowtypeYoga.purslint).
23+
24+### CLI (npm)
1625
1726```bash
1827npm install -g purslint
1928
2029# CLI
21−purelint --help
30+purslint --help
2231```
2332
24−### Prerequisites
33+### Neovim (nvim-lspconfig)
2534
26−- [PureScript](https://www.purescript.org/) compiler (`purs`)
27−- [Spago](https://github.com/purescript/spago) package manager
28−- [Go](https://golang.org/) 1.19+
29−- [psgo](https://github.com/puregocore/psgo) - PureScript to Go compiler
35+After building `purslint-lsp` (see build steps below), you can configure Neovim like this:
3036
31−### Building
37+```lua
38+require("lspconfig").purslint = {
39+ default_config = {
40+ cmd = { "/path/to/purslint-lsp" },
41+ filetypes = { "purescript" },
42+ root_dir = require("lspconfig.util").root_pattern(".git", "spago.yaml", "package.json"),
43+ },
44+}
3245
33−```bash
34−# Install dependencies
35−spago install
36−
37−# Build CLI
38−purs compile --codegen corefn 'lib/**/*.purs' 'bin/purslint-cli/**/*.purs' '.spago/**/*.purs'
39−psgo output/*/corefn.json
40−cp output/Main/Main.go main_cli.go
41−go build -o purelint main_cli.go
42−
43−# Build LSP
44−purs compile --codegen corefn 'lib/**/*.purs' 'bin/purslint-lsp/**/*.purs' '.spago/**/*.purs'
45−psgo output/*/corefn.json
46−cp output/Main/Main.go main_lsp.go
47−go build -o purelint-lsp main_lsp.go
48−
49−# Build and run tests
50−purs compile --codegen corefn 'lib/**/*.purs' 'test/**/*.purs' '.spago/**/*.purs'
51−psgo output/*/corefn.json
52−cp output/Test.Main/Test_Main.go test_main.go
53−go build -o test-runner test_main.go
54−./test-runner
46+require("lspconfig").purslint.setup({})
5547```
5648
57−## Usage
58−
59−### CLI
60−
61−```bash
62−# Lint a single file
63−./purelint src/Main.purs
64−
65−# Lint multiple files
66−./purelint src/*.purs
49+If you prefer to run the bundled JS directly:
6750
68−# Lint with config file (optional)
69−./purelint --config purelint.json src/*.purs
51+```lua
52+cmd = { "node", "/path/to/dist/purslint-lsp.js" }
7053```
7154
72−### LSP (Language Server Protocol)
73−
74−The LSP binary (`purelint-lsp`) provides IDE integration. It communicates via stdio using the standard LSP protocol.
75−
76−#### Zed Editor
55+### Zed Editor
7756
7857A Zed extension is available in the `zed-extension/` directory. To install:
7958
@@ -82,33 +61,36 @@ A Zed extension is available in the `zed-extension/` directory. To install:
82613. Click "Install Dev Extension"
83624. Select the `zed-extension/` directory
8463
85−The extension will automatically use the `purelint-lsp` binary.
64+The extension will automatically use the `purslint-lsp` binary.
8665
87−#### Neovim (nvim-lspconfig)
66+## Usage
8867
89−After building `purelint-lsp` (see build steps below), you can configure Neovim like this:
68+### CLI
9069
91−```lua
92−require("lspconfig").purelint = {
93− default_config = {
94− cmd = { "/path/to/purelint-lsp" },
95− filetypes = { "purescript" },
96− root_dir = require("lspconfig.util").root_pattern(".git", "spago.yaml", "package.json"),
97− },
98−}
70+```bash
71+# Lint a single file
72+./purslint src/Main.purs
73+
74+# Lint multiple files
75+./purslint src/*.purs
9976
100−require("lspconfig").purelint.setup({})
77+# Lint with config file (optional)
78+./purslint --config purslint.json src/*.purs
10179```
10280
103−If you prefer to run the bundled JS directly:
81+### LSP (Language Server Protocol)
10482
105−```lua
106−cmd = { "node", "/path/to/dist/purelint-lsp.js" }
107−```
83+The LSP binary (`purslint-lsp`) provides IDE integration. It communicates via stdio using the standard LSP protocol.
84+
85+## Features
86+
87+Purslint currently ships with **81 lint rules**, covering common PureScript refactors and simplifications.
88+
89+For a complete list, see `lib/purslint/src/Purslint/Rules/`.
10890
10991## Configuration
11092
111−Create a `purelint.json` file in your project root:
93+Create a `purslint.json` file in your project root:
11294
11395```json
11496{
@@ -118,16 +100,17 @@ Create a `purelint.json` file in your project root:
118100
119101## Architecture
120102
121−PureLint is written in PureScript and compiled to Go using `psgo` (purescript-native). This allows:
103+Purslint is written in PureScript and compiled to JavaScript via
104+`purs-backend-es`, then bundled for Node.js. This allows:
122105
123106- Full access to the PureScript CST parser (`purescript-language-cst-parser`)
124−- Native binary performance
125−- Easy distribution without runtime dependencies
107+- Straightforward distribution via npm
108+- A single runtime across CLI and LSP
126109
127110### Project Structure
128111
129112```
130−purelint/
113+purslint/
131114├── lib/purslint/src/ # Core library
132115│ └── Purslint/
133116│ ├── Rules/ # Lint rules
@@ -143,6 +126,25 @@ purelint/
143126
144127## Development
145128
129+### Prerequisites
130+
131+- [PureScript](https://www.purescript.org/) compiler (`purs`)
132+- [Spago](https://github.com/purescript/spago) package manager
133+- [Node.js](https://nodejs.org/) 18+
134+
135+### Building
136+
137+```bash
138+# Install dependencies
139+npm install
140+
141+# Build and bundle CLI + LSP
142+npm run dist
143+
144+# Run tests
145+npm test
146+```
147+
146148### Publishing to npm
147149
148150Publishing is handled by GitHub Actions. Add an `NPM_TOKEN` secret with an
@@ -162,6 +164,8 @@ via the workflow dispatch.
162164
163165- `purescript-language-cst-parser` - PureScript CST parser
164166- `purescript-lsp` - LSP protocol implementation (Go FFI)
167+- `purs-backend-es` - PureScript to JavaScript backend
168+- `esbuild` - Bundler for CLI and LSP
165169
166170## License
167171
bin/purelint-lsp.jsdeleted−2
@@ -1,2 +0,0 @@
1−#!/usr/bin/env node
2−import "../dist/purelint-lsp.js";
bin/purelint.jsdeleted−2
@@ -1,2 +0,0 @@
1−#!/usr/bin/env node
2−import "../dist/purelint.js";
bin/purslint-cli/spago.yaml+3−2
@@ -1,5 +1,5 @@
11package:
2− name: purelint-cli
2+ name: purslint-cli
33 dependencies:
44 - arrays
55 - console
@@ -8,8 +8,9 @@ package:
88 - foldable-traversable
99 - maybe
1010 - newtype
11+ - optparse
1112 - prelude
12− - purelint
13+ - purslint
1314 - strings
1415 - node-buffer
1516 - node-fs
bin/purslint-cli/src/Main.purs+91−46
@@ -4,14 +4,20 @@ import Prelude
44
55import Data.Array as Array
66import Data.Either (Either(..))
7+import Data.Enum (fromEnum)
78import Data.Foldable (for_)
89import Data.Maybe (Maybe(..))
910import Data.Newtype (unwrap)
1011import Data.String.CodeUnits as String
12+import Data.Tuple.Nested ((/\))
1113import Effect (Effect)
1214import Effect.Console (log)
1315import Node.FS.Simple as FS
1416import Node.CLI as Process
17+import Options.Applicative (Parser, argument, execParserPure, fullDesc, header, help, helper, info, long, many, metavar, progDesc, str, switch, (<**>))
18+import Options.Applicative.Builder (defaultPrefs)
19+import Options.Applicative.Types (CompletionResult(..), ParserFailure(ParserFailure), ParserResult(..))
20+import Options.Applicative.Help.Types (ParserHelp(ParserHelp), renderHelp)
1521import Purslint.Config (defaultConfig, filterRules, parseConfig)
1622import Purslint.Fix (applyAllFixes)
1723import Purslint.Rule (Rule)
@@ -99,6 +105,13 @@ import Purslint.Rules.RedundantParens (redundantParensRule)
99105import Purslint.Runner (runRules)
100106import Purslint.Types (LintResult(..), LintWarning(..), Severity(..), SourceCode(..), Suggestion(..))
101107
108+type CliOptions =
109+ { fix :: Boolean
110+ , listRules :: Boolean
111+ , initConfig :: Boolean
112+ , paths :: Array String
113+ }
114+
102115-- | All available rules (81 total)
103116allRules :: Array Rule
104117allRules =
@@ -185,12 +198,12 @@ allRules =
185198 , whenNotRule
186199 ]
187200
188−-- | Load config from .purelintrc file
201+-- | Load config from .purslintrc file
189202loadConfig :: Effect _
190203loadConfig = do
191− hasConfig <- FS.exists ".purelintrc"
204+ hasConfig <- FS.exists ".purslintrc"
192205 if hasConfig then do
193− content <- FS.readTextFile ".purelintrc"
206+ content <- FS.readTextFile ".purslintrc"
194207 pure $ parseConfig content
195208 else
196209 pure defaultConfig
@@ -294,24 +307,24 @@ fixPath rules path = do
294307-- | Show usage
295308showUsage :: Effect Unit
296309showUsage = do
297− log "purelint - a linter for PureScript (70 rules)"
310+ log "purslint - a linter for PureScript (81 rules)"
298311 log ""
299− log "Usage: purelint [OPTIONS] [FILES/DIRECTORIES...]"
312+ log "Usage: purslint [OPTIONS] [FILES/DIRECTORIES...]"
300313 log ""
301314 log "Options:"
302315 log " --help Show this help"
303316 log " --fix Auto-fix issues (where possible)"
304− log " --init Create a sample .purelintrc"
317+ log " --init Create a sample .purslintrc"
305318 log " --list List all available rules"
306319 log ""
307320 log "Configuration:"
308− log " Create .purelintrc in your project root to disable rules."
321+ log " Create .purslintrc in your project root to disable rules."
309322 log " Add one rule name per line (lines starting with # are comments)."
310323 log ""
311324 log "Examples:"
312− log " purelint src/ Lint all .purs files in src/"
313− log " purelint --fix src/ Auto-fix issues in src/"
314− log " purelint src/Main.purs Lint a single file"
325+ log " purslint src/ Lint all .purs files in src/"
326+ log " purslint --fix src/ Auto-fix issues in src/"
327+ log " purslint src/Main.purs Lint a single file"
315328
316329-- | List all available rules
317330listRules :: Effect Unit
@@ -324,51 +337,83 @@ listRules = do
324337-- | Create sample config file
325338initConfig :: Effect Unit
326339initConfig = do
327− hasConfig <- FS.exists ".purelintrc"
340+ hasConfig <- FS.exists ".purslintrc"
328341 if hasConfig then
329− log ".purelintrc already exists"
342+ log ".purslintrc already exists"
330343 else do
331− let sampleConfig = """# purelint configuration
344+ let sampleConfig = """# purslint configuration
332345# Add rule names (one per line) to disable them
333346# Example:
334347# EtaReduce
335348# LetToWhere
336349"""
337− FS.writeTextFile ".purelintrc" sampleConfig
338− log "Created .purelintrc"
350+ FS.writeTextFile ".purslintrc" sampleConfig
351+ log "Created .purslintrc"
352+
353+parseFlags :: Array String -> Effect (Maybe CliOptions)
354+parseFlags args = case parseFlags' (Array.drop 2 args) of
355+ Success cli -> pure (Just cli)
356+ Failure (ParserFailure pf) -> case pf programName of
357+ ph@(ParserHelp _) /\ exitCode /\ i /\ _unit -> do
358+ log $ renderHelp i ph
359+ Process.exit (fromEnum exitCode)
360+ pure Nothing
361+ CompletionInvoked (CompletionResult { execCompletion }) -> do
362+ executedCompletion <- execCompletion programName
363+ log executedCompletion
364+ Process.exit 0
365+ pure Nothing
366+ where
367+ programName = "purslint"
368+
369+parseFlags' :: Array String -> ParserResult CliOptions
370+parseFlags' = execParserPure defaultPrefs opts
371+ where
372+ opts =
373+ info (flagsParser <**> helper) $
374+ fullDesc
375+ <> progDesc "Lint PureScript source files"
376+ <> header "purslint - a linter for PureScript"
377+
378+flagsParser :: Parser CliOptions
379+flagsParser = ado
380+ fix <- switch $ long "fix" <> help "Auto-fix issues (where possible)"
381+ listRules <- switch $ long "list" <> help "List all available rules"
382+ initConfig <- switch $ long "init" <> help "Create a sample .purslintrc"
383+ pathsList <- many $ argument str $ metavar "PATH..."
384+ let paths = Array.fromFoldable pathsList
385+ in { fix, listRules, initConfig, paths }
339386
340387main :: Effect Unit
341388main = do
342389 args <- Process.argv
343− -- args[0] is node, args[1] is the script, args[2..] are the actual arguments
344− let userArgs = Array.drop 1 args
345− case userArgs of
346− [] -> showUsage
347− ["--help"] -> showUsage
348− ["-h"] -> showUsage
349− ["--list"] -> listRules
350− ["--init"] -> initConfig
351− _ -> do
352− -- Check for --fix flag
353− let hasFix = Array.elem "--fix" userArgs
354− let paths = Array.filter (\a -> a /= "--fix") userArgs
355−
356− -- Load config and filter rules
357− config <- loadConfig
358− let enabledRules = filterRules allRules config
359−
360− if hasFix then do
361− -- Fix mode
362− _ <- Array.foldM (\acc path -> do
363− count <- fixPath enabledRules path
364− pure (acc + count)) 0 paths
365− pure unit
390+ maybeCli <- parseFlags args
391+ case maybeCli of
392+ Nothing -> pure unit
393+ Just cli ->
394+ if cli.listRules then
395+ listRules
396+ else if cli.initConfig then
397+ initConfig
398+ else if Array.null cli.paths then
399+ showUsage
366400 else do
367− -- Lint mode
368− totalWarnings <- Array.foldM (\acc path -> do
369− count <- lintPathWithConfig enabledRules path
370− pure (acc + count)) 0 paths
371− when (totalWarnings > 0) do
372− log ""
373− log $ "Total: " <> show totalWarnings <> " warning" <> if totalWarnings == 1 then "" else "s"
374− Process.exit 1
401+ -- Load config and filter rules
402+ config <- loadConfig
403+ let enabledRules = filterRules allRules config
404+
405+ if cli.fix then do
406+ -- Fix mode
407+ _ <- Array.foldM (\acc path -> do
408+ count <- fixPath enabledRules path
409+ pure (acc + count)) 0 cli.paths
410+ pure unit
411+ else do
412+ -- Lint mode
413+ totalWarnings <- Array.foldM (\acc path -> do
414+ count <- lintPathWithConfig enabledRules path
415+ pure (acc + count)) 0 cli.paths
416+ when (totalWarnings > 0) do
417+ log ""
418+ log $ "Total: " <> show totalWarnings <> " warning" <> if totalWarnings == 1 then "" else "s"
419+ Process.exit 1
bin/purslint-lsp.jsnew file+2
@@ -0,0 +1,2 @@
1+#!/usr/bin/env node
2+import "../dist/purslint-lsp.js";
bin/purslint-lsp/spago.yaml+2−2
@@ -1,5 +1,5 @@
11package:
2− name: purelint-lsp
2+ name: purslint-lsp
33 dependencies:
44 - arrays
55 - effect
@@ -8,5 +8,5 @@ package:
88 - maybe
99 - newtype
1010 - prelude
11− - purelint
11+ - purslint
1212 - unsafe-coerce
bin/purslint-lsp/src/LSP.purs+18−18
@@ -208,7 +208,7 @@ warningToDiagnostic (LintWarning w) =
208208 base =
209209 { range: range
210210 , severity: severityToLSP w.severity
211− , source: "purelint"
211+ , source: "purslint"
212212 , message: unwrap w.ruleId <> ": " <> unwrap w.message
213213 }
214214 in case w.suggestion of
@@ -216,7 +216,7 @@ warningToDiagnostic (LintWarning w) =
216216 Just (Suggestion sug) -> unsafeCoerce
217217 { range: range
218218 , severity: severityToLSP w.severity
219− , source: "purelint"
219+ , source: "purslint"
220220 , message: unwrap w.ruleId <> ": " <> unwrap w.message
221221 , data:
222222 { hasFix: true
@@ -273,7 +273,7 @@ mkInitializeResponse reqId =
273273 }
274274 }
275275 , serverInfo:
276− { name: "purelint-lsp"
276+ { name: "purslint-lsp"
277277 , version: "0.1.0"
278278 }
279279 }
@@ -334,32 +334,32 @@ foreign import hasKey :: String -> Foreign -> Boolean
334334foreign import getKey :: String -> Foreign -> Foreign
335335
336336-- | Parse settings to get disabled rules
337−-- | Expected format: { "purelint": { "disabledRules": ["RuleId1", "RuleId2"] } }
338−-- | Or: { "purelint.disabledRules": ["RuleId1", "RuleId2"] }
339−-- | Or: { "purelint.enabled": false } to disable all rules
337+-- | Expected format: { "purslint": { "disabledRules": ["RuleId1", "RuleId2"] } }
338+-- | Or: { "purslint.disabledRules": ["RuleId1", "RuleId2"] }
339+-- | Or: { "purslint.enabled": false } to disable all rules
340340parseSettings :: Foreign -> Config
341341parseSettings settings =
342− -- Try to get purelint.enabled first
343− case getFieldObj "purelint" settings of
344− Just purelintObj ->
342+ -- Try to get purslint.enabled first
343+ case getFieldObj "purslint" settings of
344+ Just purslintObj ->
345345 -- Check if all rules are disabled
346− case getFieldBool "enabled" purelintObj of
346+ case getFieldBool "enabled" purslintObj of
347347 Just false ->
348348 { disabledRules: map (\r -> r.ruleId) allRules } -- Disable all rules
349349 _ ->
350350 -- Get disabled rules list
351− case getFieldArr "disabledRules" purelintObj of
351+ case getFieldArr "disabledRules" purslintObj of
352352 Just arr ->
353353 { disabledRules: Array.mapMaybe parseRuleId arr }
354354 Nothing -> defaultConfig
355355 Nothing ->
356− -- Try flat format: purelint.disabledRules
357− case getFieldArr "purelint.disabledRules" settings of
356+ -- Try flat format: purslint.disabledRules
357+ case getFieldArr "purslint.disabledRules" settings of
358358 Just arr ->
359359 { disabledRules: Array.mapMaybe parseRuleId arr }
360360 Nothing ->
361− -- Try purelint.enabled
362− case getFieldBool "purelint.enabled" settings of
361+ -- Try purslint.enabled
362+ case getFieldBool "purslint.enabled" settings of
363363 Just false ->
364364 { disabledRules: map (\r -> r.ruleId) allRules }
365365 _ -> defaultConfig
@@ -616,7 +616,7 @@ mkDisableRuleAction ruleId diagnostic =
616616 , isPreferred: false
617617 , command:
618618 { title: "Disable rule " <> ruleId
619− , command: "purelint.disableRule"
619+ , command: "purslint.disableRule"
620620 , arguments: [ruleId]
621621 }
622622 }
@@ -644,7 +644,7 @@ mainLoop stateRef = do
644644
645645main :: Effect Unit
646646main = do
647− Stdio.logMessage "purelint-lsp starting..."
647+ Stdio.logMessage "purslint-lsp starting..."
648648 stateRef <- Ref.new { config: defaultConfig, documents: Map.empty }
649649 mainLoop stateRef
650− Stdio.logMessage "purelint-lsp exiting"
650+ Stdio.logMessage "purslint-lsp exiting"
bin/purslint-lsp/src/LSP/Stdio.js+1−1
@@ -74,7 +74,7 @@ export const writeMessage = (msg) => () => {
7474
7575// Log a message to stderr (for debugging)
7676export const logMessage = (msg) => () => {
77− process.stderr.write('[purelint-lsp] ' + msg + '\n');
77+ process.stderr.write('[purslint-lsp] ' + msg + '\n');
7878};
7979
8080// Parse JSON string to Foreign (returns Maybe Foreign)
bin/purslint.jsnew file+2
@@ -0,0 +1,2 @@
1+#!/usr/bin/env node
2+import "../dist/purslint.js";
demo.gifnew file

Binary file not shown.

lib/purslint/spago.yaml+1−1
@@ -1,5 +1,5 @@
11package:
2− name: purelint
2+ name: purslint
33 dependencies:
44 - arrays
55 - effect
lib/purslint/src/Purslint/Fix.purs+12−9
@@ -71,8 +71,9 @@ applyFix :: String -> LintWarning -> Maybe String
7171applyFix source (LintWarning w) = do
7272 Suggestion s <- w.suggestion
7373 let replacement = s.replacement # un ReplacementText
74− let { source: updatedSource, replacement: updatedReplacement, linesAdded } =
75− processImports source replacement s.requiredImports
74+ let
75+ { source: updatedSource, replacement: updatedReplacement, linesAdded } =
76+ processImports source replacement s.requiredImports
7677 let shiftedRange = shiftRange linesAdded w.range
7778 Just $ replaceRange updatedSource shiftedRange updatedReplacement
7879
@@ -101,13 +102,15 @@ applyAllFixes source (LintResult result) =
101102 else case w.suggestion of
102103 Nothing -> acc
103104 Just (Suggestion s) ->
104− let replacement = s.replacement # un ReplacementText
105− importResult = processImports acc.source replacement s.requiredImports
106− shiftedRange = shiftRange importResult.linesAdded w.range
107− updatedSource = replaceRange importResult.source shiftedRange importResult.replacement
108− in { source: updatedSource
109− , appliedRanges: Array.cons shiftedRange acc.appliedRanges
110− }
105+ let
106+ replacement = s.replacement # un ReplacementText
107+ importResult = processImports acc.source replacement s.requiredImports
108+ shiftedRange = shiftRange importResult.linesAdded w.range
109+ updatedSource = replaceRange importResult.source shiftedRange importResult.replacement
110+ in
111+ { source: updatedSource
112+ , appliedRanges: Array.cons shiftedRange acc.appliedRanges
113+ }
111114
112115 -- Check if two ranges overlap
113116 overlaps :: SourceRange -> SourceRange -> Boolean
package.json+6−6
@@ -4,15 +4,15 @@
44 "description": "A linter for PureScript",
55 "type": "module",
66 "bin": {
7− "purelint": "./bin/purelint.js",
8− "purelint-lsp": "./bin/purelint-lsp.js"
7+ "purslint": "./bin/purslint.js",
8+ "purslint-lsp": "./bin/purslint-lsp.js"
99 },
1010 "scripts": {
11− "build:cli": "spago build -p purelint-cli --output output-cli",
12− "build:lsp": "spago build -p purelint-lsp --output output-lsp",
11+ "build:cli": "spago build -p purslint-cli --output output-cli",
12+ "build:lsp": "spago build -p purslint-lsp --output output-lsp",
1313 "build": "npm run build:cli && npm run build:lsp",
14− "bundle:cli": "purs-backend-es bundle-app --corefn-dir output-cli --main Main --platform node --to dist/purelint.js --int-tags",
15− "bundle:lsp": "purs-backend-es bundle-app --corefn-dir output-lsp --main Main.LSP --platform node --to dist/purelint-lsp.js --int-tags",
14+ "bundle:cli": "purs-backend-es bundle-app --corefn-dir output-cli --main Main --platform node --to dist/purslint.js --int-tags",
15+ "bundle:lsp": "purs-backend-es bundle-app --corefn-dir output-lsp --main Main.LSP --platform node --to dist/purslint-lsp.js --int-tags",
1616 "bundle": "npm run bundle:cli && npm run bundle:lsp",
1717 "dist": "npm run build && npm run bundle",
1818 "test": "spago test",
purelint-lspdeleted−2
@@ -1,2 +0,0 @@
1−#!/bin/bash
2−exec node /Users/mark/Developer/purelint/dist/purelint-lsp.js "$@"
purslint-lspnew file+2
@@ -0,0 +1,2 @@
1+#!/bin/bash
2+exec node "$(cd "$(dirname "$0")" && pwd)/dist/purslint-lsp.js" "$@"
spago.lock+91−10
@@ -1,7 +1,7 @@
11{
22 "workspace": {
33 "packages": {
4− "purelint": {
4+ "purslint": {
55 "path": "lib/purslint",
66 "core": {
77 "dependencies": [
@@ -69,7 +69,7 @@
6969 "build_plan": []
7070 }
7171 },
72− "purelint-cli": {
72+ "purslint-cli": {
7373 "path": "bin/purslint-cli",
7474 "core": {
7575 "dependencies": [
@@ -83,8 +83,9 @@
8383 "node-buffer",
8484 "node-fs",
8585 "node-process",
86+ "optparse",
8687 "prelude",
87− "purelint",
88+ "purslint",
8889 "strings"
8990 ],
9091 "build_plan": [
@@ -104,6 +105,7 @@
104105 "enums",
105106 "exceptions",
106107 "exists",
108+ "exitcodes",
107109 "foldable-traversable",
108110 "foreign",
109111 "foreign-object",
@@ -130,6 +132,8 @@
130132 "now",
131133 "nullable",
132134 "numbers",
135+ "open-memoize",
136+ "optparse",
133137 "ordered-collections",
134138 "orders",
135139 "parallel",
@@ -137,7 +141,7 @@
137141 "posix-types",
138142 "prelude",
139143 "profunctor",
140− "purelint",
144+ "purslint",
141145 "refs",
142146 "safe-coerce",
143147 "st",
@@ -156,7 +160,7 @@
156160 "build_plan": []
157161 }
158162 },
159− "purelint-lsp": {
163+ "purslint-lsp": {
160164 "path": "bin/purslint-lsp",
161165 "core": {
162166 "dependencies": [
@@ -167,7 +171,7 @@
167171 "maybe",
168172 "newtype",
169173 "prelude",
170− "purelint",
174+ "purslint",
171175 "unsafe-coerce"
172176 ],
173177 "build_plan": [
@@ -204,7 +208,7 @@
204208 "partial",
205209 "prelude",
206210 "profunctor",
207− "purelint",
211+ "purslint",
208212 "refs",
209213 "safe-coerce",
210214 "st",
@@ -223,7 +227,7 @@
223227 "build_plan": []
224228 }
225229 },
226− "purelint-tests": {
230+ "purslint-tests": {
227231 "path": "./",
228232 "core": {
229233 "dependencies": [
@@ -233,10 +237,13 @@
233237 "either",
234238 "maybe",
235239 "newtype",
240+ "optparse",
236241 "prelude",
237− "purelint"
242+ "purslint"
238243 ],
239244 "build_plan": [
245+ "aff",
246+ "arraybuffer-types",
240247 "arrays",
241248 "bifunctors",
242249 "catenable-lists",
@@ -244,13 +251,17 @@
244251 "const",
245252 "contravariant",
246253 "control",
254+ "datetime",
247255 "distributive",
248256 "effect",
249257 "either",
250258 "enums",
251259 "exceptions",
252260 "exists",
261+ "exitcodes",
253262 "foldable-traversable",
263+ "foreign",
264+ "foreign-object",
254265 "free",
255266 "functions",
256267 "functors",
@@ -263,14 +274,23 @@
263274 "lists",
264275 "maybe",
265276 "newtype",
277+ "node-buffer",
278+ "node-event-emitter",
279+ "node-process",
280+ "node-streams",
266281 "nonempty",
282+ "nullable",
267283 "numbers",
284+ "open-memoize",
285+ "optparse",
268286 "ordered-collections",
269287 "orders",
288+ "parallel",
270289 "partial",
290+ "posix-types",
271291 "prelude",
272292 "profunctor",
273− "purelint",
293+ "purslint",
274294 "refs",
275295 "safe-coerce",
276296 "st",
@@ -1075,6 +1095,14 @@
10751095 "unsafe-coerce"
10761096 ]
10771097 },
1098+ "exitcodes": {
1099+ "type": "registry",
1100+ "version": "4.0.0",
1101+ "integrity": "sha256-4wxViTbyOoyKJ/WaRGI6+hZmgMKI5Miv16lSwefiLSM=",
1102+ "dependencies": [
1103+ "enums"
1104+ ]
1105+ },
10781106 "foldable-traversable": {
10791107 "type": "registry",
10801108 "version": "6.0.0",
@@ -1458,6 +1486,59 @@
14581486 "maybe"
14591487 ]
14601488 },
1489+ "open-memoize": {
1490+ "type": "registry",
1491+ "version": "6.2.0",
1492+ "integrity": "sha256-p1m7wF3aHQ80yUvqMs20OTMl496WS6YpKlmI2Nkg9j0=",
1493+ "dependencies": [
1494+ "either",
1495+ "integers",
1496+ "lazy",
1497+ "lists",
1498+ "maybe",
1499+ "partial",
1500+ "prelude",
1501+ "strings",
1502+ "tuples"
1503+ ]
1504+ },
1505+ "optparse": {
1506+ "type": "registry",
1507+ "version": "5.0.1",
1508+ "integrity": "sha256-cEzEkNW4q0gZlXl4z0zn+H2vs6l2UAp7NPHCsois73k=",
1509+ "dependencies": [
1510+ "aff",
1511+ "arrays",
1512+ "bifunctors",
1513+ "console",
1514+ "control",
1515+ "effect",
1516+ "either",
1517+ "enums",
1518+ "exists",
1519+ "exitcodes",
1520+ "foldable-traversable",
1521+ "free",
1522+ "gen",
1523+ "integers",
1524+ "lazy",
1525+ "lists",
1526+ "maybe",
1527+ "newtype",
1528+ "node-buffer",
1529+ "node-process",
1530+ "node-streams",
1531+ "nonempty",
1532+ "numbers",
1533+ "open-memoize",
1534+ "partial",
1535+ "prelude",
1536+ "strings",
1537+ "tailrec",
1538+ "transformers",
1539+ "tuples"
1540+ ]
1541+ },
14611542 "ordered-collections": {
14621543 "type": "registry",
14631544 "version": "3.2.0",
spago.yaml+3−2
@@ -4,15 +4,16 @@ workspace:
44 extraPackages: {}
55
66package:
7− name: purelint-tests
7+ name: purslint-tests
88 dependencies:
99 - console
1010 - effect
1111 - either
1212 - maybe
1313 - newtype
14+ - optparse
1415 - prelude
15− - purelint
16+ - purslint
1617 - arrays
1718 test:
1819 main: Test.Main
test/Test/Main.purs+1−1
@@ -175,7 +175,7 @@ assertFixFormatted name rules source expectedFixed =
175175-- | Run all tests and report results
176176main :: Effect Unit
177177main = do
178− log "Running purelint tests...\n"
178+ log "Running purslint tests...\n"
179179
180180 let results = mapIdentityTests
181181 <> mapFusionTests
vscode-extension/out/extension.js+2−2
@@ -46,8 +46,8 @@ function activate(context) {
4646 // Get server path from config or use default
4747 let serverPath = config.get('serverPath', '');
4848 if (!serverPath) {
49− // Default to the purelint project location
50− serverPath = '/Users/mark/Developer/purelint/dist/purelint-lsp.js';
49+ // Default to the purslint project location
50+ serverPath = '/Users/mark/Developer/purslint/dist/purslint-lsp.js';
5151 }
5252 const serverOptions = {
5353 run: {
vscode-extension/package.json+1−1
@@ -4,7 +4,7 @@
44 "description": "PureScript linter inspired by hlint",
55 "version": "0.1.1",
66 "publisher": "RowtypeYoga",
7− "icon": "purslint.jpg",
7+ "icon": "purslint.png",
88 "engines": {
99 "vscode": "^1.85.0"
1010 },
vscode-extension/purslint.jpgdeleted

Binary file not shown.

vscode-extension/purslint.pngnew file

Binary file not shown.

vscode-extension/src/extension.ts+2−2
@@ -19,8 +19,8 @@ export function activate(context: vscode.ExtensionContext) {
1919 // Get server path from config or use default
2020 let serverPath = config.get<string>('serverPath', '');
2121 if (!serverPath) {
22− // Default to the purelint project location
23− serverPath = '/Users/mark/Developer/purelint/dist/purelint-lsp.js';
22+ // Default to the purslint project location
23+ serverPath = '/Users/mark/Developer/purslint/dist/purslint-lsp.js';
2424 }
2525
2626 const serverOptions: ServerOptions = {
zed-extension/Cargo.lock+3−3
@@ -412,7 +412,7 @@ dependencies = [
412412]
413413
414414[[package]]
415−name = "purelint-zed"
415+name = "purslint-zed"
416416version = "0.0.1"
417417dependencies = [
418418 "zed_extension_api",
@@ -818,6 +818,6 @@ dependencies = [
818818
819819[[package]]
820820name = "zmij"
821−version = "1.0.16"
821+version = "1.0.17"
822822source = "registry+https://github.com/rust-lang/crates.io-index"
823−checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65"
823+checksum = "02aae0f83f69aafc94776e879363e9771d7ecbffe2c7fbb6c14c5e00dfe88439"
zed-extension/Cargo.toml+1−1
@@ -1,5 +1,5 @@
11[package]
2−name = "purelint-zed"
2+name = "purslint-zed"
33version = "0.0.1"
44edition = "2021"
55
zed-extension/extension.toml+3−3
@@ -1,5 +1,5 @@
1−id = "purelint"
2−name = "Purelint"
1+id = "purslint"
2+name = "Purslint"
33version = "0.0.1"
44schema_version = 1
55authors = ["Mark"]
@@ -9,5 +9,5 @@ description = "PureScript linter inspired by hlint"
99kind = "Rust"
1010version = "0.7.0"
1111
12−[language_servers.purelint]
12+[language_servers.purslint]
1313language = "PureScript"
zed-extension/src/lib.rs+5−5
@@ -1,10 +1,10 @@
11use zed_extension_api::{self as zed, Result};
22
3−struct PurelintExtension;
3+struct PurslintExtension;
44
5−impl zed::Extension for PurelintExtension {
5+impl zed::Extension for PurslintExtension {
66 fn new() -> Self {
7− PurelintExtension
7+ PurslintExtension
88 }
99
1010 fn language_server_command(
@@ -13,11 +13,11 @@ impl zed::Extension for PurelintExtension {
1313 _worktree: &zed::Worktree,
1414 ) -> Result<zed::Command> {
1515 Ok(zed::Command {
16− command: "/Users/mark/Developer/purelint/purelint-lsp".to_string(),
16+ command: "purslint-lsp".to_string(),
1717 args: vec![],
1818 env: Default::default(),
1919 })
2020 }
2121}
2222
23−zed::register_extension!(PurelintExtension);
23+zed::register_extension!(PurslintExtension);