Skip to content

Commit 767f60f

Browse files
authored
Merge branch 'master' into change-type-signature
2 parents 1d80aae + 83d26ec commit 767f60f

File tree

18 files changed

+262
-156
lines changed

18 files changed

+262
-156
lines changed

.github/actions/setup-build/action.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,19 @@ runs:
6363
fi
6464
shell: bash
6565

66+
# The default build for haskell-language-server executable is dynamic for linux and macOS
67+
# to get a executable which works for Template Haskell
68+
# However we continue providing full static executables in releases so we have to disable it
69+
# *for all workflows*, including test, flags and release builds
70+
- name: Disable -dynamic
71+
run: |
72+
echo -e "package haskell-language-server\n flags: -dynamic" >> cabal.project.local
73+
shell: bash
74+
6675
- if: inputs.os == 'Windows' && inputs.ghc == '8.8.4'
6776
name: (Windows,GHC 8.8) Modify `cabal.project` to workaround segfaults
6877
run: |
69-
echo -e 'package floskell\n ghc-options: -O0' >> cabal.project.local
78+
echo -e 'package floskell\n ghc-options: -O0' >> cabal.project.local
7079
shell: bash
7180

7281
# Shorten binary names as a workaround for filepath length limits in Windows,

.github/workflows/build.yml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,6 @@ jobs:
4848
echo "tests: false" >> cabal.project.local
4949
echo "benchmarks: false" >> cabal.project.local
5050
51-
- name: Disable -dynamic
52-
run: |
53-
echo "package haskell-language-server" >> cabal.project.local
54-
echo " flags: -dynamic" >> cabal.project.local
55-
5651
- uses: ./.github/actions/setup-build
5752
with:
5853
ghc: ${{ matrix.ghc }}

ChangeLog.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# Changelog for haskell-language-server
22

3+
## 1.6.1.1 (*only hackage release*)
4+
5+
- Release to update haskell-language-server.cabal in hackage, setting the build for the executable component as dynamically linked
6+
- The motivation is build by default a hls executable which works for Template Haskell
7+
- This doesn't need a full release cause it does not affect release executables which continue being fully static
8+
9+
### Pull requests merged for 1.6.1.1
10+
11+
- Prepare 1.6.1.1 (only hackage release)
12+
([#2681](https://github.com/haskell/haskell-language-server/pull/2681)) by @jneira
13+
- Add the -dynamic flag and update build instructions
14+
([#2668](https://github.com/haskell/haskell-language-server/pull/2668)) by @pepeiborra
15+
316
## 1.6.1.0
417

518
This is a bug fix release to restore a fully statically linked haskell-language-server-wrapper executable.

docs/features.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ Uses [apply-refact](https://github.com/mpickering/apply-refact).
144144
Known limitations:
145145

146146
- May have strange behaviour in files with CPP, since `apply-refact` does not support CPP.
147+
- The `hlint` executable by default turns on many extensions when parsing a file because it is not certain about the exact extensions that apply to the file (they may come from project files). This differs from HLS which uses only the extensions the file needs to parse the file. Hence it is possible for the `hlint` executable to report a parse error on a file, but the `hlint` plugin to work just fine on the same file. This does mean that the turning on/off of extensions in the hlint config may be ignored by the `hlint` plugin.
148+
- Hlint restrictions do not work (yet). This [PR](https://github.com/ndmitchell/hlint/pull/1340) should enable that functionality, but this requires a newer version of hlint to be used in HLS.
147149

148150
### Make import lists fully explicit
149151

docs/troubleshooting.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -160,27 +160,27 @@ The server log will show which cradle is being chosen.
160160

161161
Using an explicit `hie.yaml` to configure the cradle can resolve the problem, see the [configuration page](./configuration.md#configuring-your-project-build).
162162

163-
### Static binaries and template haskell support
163+
### Static binaries
164164

165-
Static binaries use the GHC linker for dynamically loading dependencies when typechecking TH code, and this can run into issues when loading shared objects linked against mismatching system libraries, or into GHC linker bugs (mainly the Mach linker used in Mac OS, but also potentially the ELF linker).
165+
Static binaries use the GHC linker for dynamically loading dependencies when typechecking Template Haskell code, and this can run into issues when loading shared objects linked against mismatching system libraries, or into GHC linker bugs (mainly the Mach linker used in Mac OS, but also potentially the ELF linker).
166166
Dynamically linked binaries (including`ghci`) use the system linker instead of the GHC linker and avoid both issues.
167167

168168
The easiest way to obtain a dynamically linked HLS binary is to build HLS locally. With `cabal` this can be done as follows:
169169

170170
```bash
171-
cabal update && cabal install pkg:haskell-language-server"
171+
cabal update && cabal install pkg:haskell-language-server
172172
```
173173

174174
If you are compiling with a ghc version with a specific `cabal-ghc${ghcVersion}.project` in the repo you will have to use it. For example for ghc-9.0.x:
175175

176176
```bash
177-
cabal update && cabal install pkg:haskell-language-server --project-file=cabal-ghc90.project"
177+
cabal update && cabal install pkg:haskell-language-server --project-file=cabal-ghc90.project
178178
```
179179

180180
Or with `stack`:
181181

182182
```bash
183-
stack install haskell-language-server --stack-yaml=stack-${ghcVersion}.yaml"
183+
stack install haskell-language-server --stack-yaml=stack-${ghcVersion}.yaml
184184
```
185185

186186
You also can leverage `ghcup compile hls`:

haskell-language-server.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
cabal-version: 2.4
22
category: Development
33
name: haskell-language-server
4-
version: 1.6.1.0
4+
version: 1.6.1.1
55
synopsis: LSP server for GHC
66
description:
77
Please see the README on GitHub at <https://github.com/haskell/haskell-language-server#readme>

plugins/hls-eval-plugin/hls-eval-plugin.cabal

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ library
4545
other-modules:
4646
Ide.Plugin.Eval.Code
4747
Ide.Plugin.Eval.CodeLens
48+
Ide.Plugin.Eval.Config
4849
Ide.Plugin.Eval.GHC
4950
Ide.Plugin.Eval.Parse.Comments
5051
Ide.Plugin.Eval.Parse.Option
@@ -105,10 +106,12 @@ test-suite tests
105106
build-depends:
106107
, aeson
107108
, base
109+
, containers
108110
, directory
109111
, extra
110112
, filepath
111113
, hls-eval-plugin
114+
, hls-plugin-api
112115
, hls-test-utils ^>=1.2
113116
, lens
114117
, lsp-types

plugins/hls-eval-plugin/src/Ide/Plugin/Eval.hs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,23 @@ module Ide.Plugin.Eval (
1111

1212
import Development.IDE (IdeState)
1313
import qualified Ide.Plugin.Eval.CodeLens as CL
14+
import Ide.Plugin.Eval.Config
1415
import Ide.Plugin.Eval.Rules (rules)
15-
import Ide.Types (PluginDescriptor (..), PluginId,
16+
import Ide.Types (ConfigDescriptor (..),
17+
PluginDescriptor (..), PluginId,
18+
defaultConfigDescriptor,
1619
defaultPluginDescriptor,
17-
mkPluginHandler)
20+
mkCustomConfig, mkPluginHandler)
1821
import Language.LSP.Types
1922

2023
-- |Plugin descriptor
2124
descriptor :: PluginId -> PluginDescriptor IdeState
2225
descriptor plId =
2326
(defaultPluginDescriptor plId)
2427
{ pluginHandlers = mkPluginHandler STextDocumentCodeLens CL.codeLens
25-
, pluginCommands = [CL.evalCommand]
28+
, pluginCommands = [CL.evalCommand plId]
2629
, pluginRules = rules
30+
, pluginConfigDescriptor = defaultConfigDescriptor
31+
{ configCustomConfig = mkCustomConfig properties
32+
}
2733
}
Lines changed: 120 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,120 +1,120 @@
1-
{-# LANGUAGE LambdaCase #-}
2-
{-# LANGUAGE OverloadedStrings #-}
3-
{-# LANGUAGE ViewPatterns #-}
4-
{-# OPTIONS_GHC -Wwarn -fno-warn-orphans #-}
5-
6-
-- | Expression execution
7-
module Ide.Plugin.Eval.Code (Statement, testRanges, resultRange, evalSetup, propSetup, testCheck, asStatements,myExecStmt) where
8-
9-
import Control.Lens ((^.))
10-
import Control.Monad.IO.Class
11-
import Data.Algorithm.Diff (Diff, PolyDiff (..), getDiff)
12-
import qualified Data.List.NonEmpty as NE
13-
import Data.String (IsString)
14-
import qualified Data.Text as T
15-
import Development.IDE.GHC.Compat
16-
import Development.IDE.Types.Location (Position (..), Range (..))
17-
import GHC (ExecOptions, ExecResult (..),
18-
execStmt)
19-
import Ide.Plugin.Eval.Types (Language (Plain), Loc,
20-
Located (..),
21-
Section (sectionLanguage),
22-
Test (..), Txt, locate,
23-
locate0)
24-
import Language.LSP.Types.Lens (line, start)
25-
import System.IO.Extra (newTempFile, readFile')
26-
27-
-- | Return the ranges of the expression and result parts of the given test
28-
testRanges :: Test -> (Range, Range)
29-
testRanges tst =
30-
let startLine = testRange tst ^. start.line
31-
(fromIntegral -> exprLines, fromIntegral -> resultLines) = testLengths tst
32-
resLine = startLine + exprLines
33-
in ( Range
34-
(Position startLine 0)
35-
--(Position (startLine + exprLines + resultLines) 0),
36-
(Position resLine 0)
37-
, Range (Position resLine 0) (Position (resLine + resultLines) 0)
38-
)
39-
40-
{- |The document range where a test is defined
41-
testRange :: Loc Test -> Range
42-
testRange = fst . testRanges
43-
-}
44-
45-
-- |The document range where the result of the test is defined
46-
resultRange :: Test -> Range
47-
resultRange = snd . testRanges
48-
49-
-- TODO: handle BLANKLINE
50-
{-
51-
>>> showDiffs $ getDiff ["abc","def","ghi","end"] ["abc","def","Z","ZZ","end"]
52-
["abc","def","WAS ghi","NOW Z","NOW ZZ","end"]
53-
-}
54-
showDiffs :: (Semigroup a, IsString a) => [Diff a] -> [a]
55-
showDiffs = map showDiff
56-
57-
showDiff :: (Semigroup a, IsString a) => Diff a -> a
58-
showDiff (First w) = "WAS " <> w
59-
showDiff (Second w) = "NOW " <> w
60-
showDiff (Both w _) = w
61-
62-
testCheck :: (Section, Test) -> [T.Text] -> [T.Text]
63-
testCheck (section, test) out
64-
| null (testOutput test) || sectionLanguage section == Plain = out
65-
| otherwise = showDiffs $ getDiff (map T.pack $ testOutput test) out
66-
67-
testLengths :: Test -> (Int, Int)
68-
testLengths (Example e r _) = (NE.length e, length r)
69-
testLengths (Property _ r _) = (1, length r)
70-
71-
-- |A one-line Haskell statement
72-
type Statement = Loc String
73-
74-
asStatements :: Test -> [Statement]
75-
asStatements lt = locate $ Located (fromIntegral $ testRange lt ^. start.line) (asStmts lt)
76-
77-
asStmts :: Test -> [Txt]
78-
asStmts (Example e _ _) = NE.toList e
79-
asStmts (Property t _ _) =
80-
["prop11 = " ++ t, "(propEvaluation prop11 :: IO String)"]
81-
82-
83-
-- |GHC declarations required for expression evaluation
84-
evalSetup :: Ghc ()
85-
evalSetup = do
86-
preludeAsP <- parseImportDecl "import qualified Prelude as P"
87-
context <- getContext
88-
setContext (IIDecl preludeAsP : context)
89-
90-
-- | A wrapper of 'InteractiveEval.execStmt', capturing the execution result
91-
myExecStmt :: String -> ExecOptions -> Ghc (Either String (Maybe String))
92-
myExecStmt stmt opts = do
93-
(temp, purge) <- liftIO newTempFile
94-
evalPrint <- head <$> runDecls ("evalPrint x = P.writeFile "<> show temp <> " (P.show x)")
95-
modifySession $ \hsc -> hsc {hsc_IC = setInteractivePrintName (hsc_IC hsc) evalPrint}
96-
result <- execStmt stmt opts >>= \case
97-
ExecComplete (Left err) _ -> pure $ Left $ show err
98-
ExecComplete (Right _) _ -> liftIO $ Right . (\x -> if null x then Nothing else Just x) <$> readFile' temp
99-
ExecBreak{} -> pure $ Right $ Just "breakpoints are not supported"
100-
liftIO purge
101-
pure result
102-
103-
{- |GHC declarations required to execute test properties
104-
105-
Example:
106-
107-
prop> \(l::[Bool]) -> reverse (reverse l) == l
108-
+++ OK, passed 100 tests.
109-
110-
prop> \(l::[Bool]) -> reverse l == l
111-
*** Failed! Falsified (after 6 tests and 2 shrinks):
112-
[True,False]
113-
-}
114-
propSetup :: [Loc [Char]]
115-
propSetup =
116-
locate0
117-
[ ":set -XScopedTypeVariables -XExplicitForAll"
118-
, "import qualified Test.QuickCheck as Q11"
119-
, "propEvaluation p = Q11.quickCheckWithResult Q11.stdArgs p >>= error . Q11.output" -- uses `error` to get a multi-line display
120-
]
1+
{-# LANGUAGE LambdaCase #-}
2+
{-# LANGUAGE OverloadedStrings #-}
3+
{-# LANGUAGE ViewPatterns #-}
4+
{-# OPTIONS_GHC -Wwarn -fno-warn-orphans #-}
5+
6+
-- | Expression execution
7+
module Ide.Plugin.Eval.Code (Statement, testRanges, resultRange, evalSetup, propSetup, testCheck, asStatements,myExecStmt) where
8+
9+
import Control.Lens ((^.))
10+
import Control.Monad.IO.Class
11+
import Data.Algorithm.Diff (Diff, PolyDiff (..), getDiff)
12+
import qualified Data.List.NonEmpty as NE
13+
import Data.String (IsString)
14+
import qualified Data.Text as T
15+
import Development.IDE.GHC.Compat
16+
import Development.IDE.Types.Location (Position (..), Range (..))
17+
import GHC (ExecOptions, ExecResult (..),
18+
execStmt)
19+
import Ide.Plugin.Eval.Types (Language (Plain), Loc,
20+
Located (..),
21+
Section (sectionLanguage),
22+
Test (..), Txt, locate,
23+
locate0)
24+
import Language.LSP.Types.Lens (line, start)
25+
import System.IO.Extra (newTempFile, readFile')
26+
27+
-- | Return the ranges of the expression and result parts of the given test
28+
testRanges :: Test -> (Range, Range)
29+
testRanges tst =
30+
let startLine = testRange tst ^. start.line
31+
(fromIntegral -> exprLines, fromIntegral -> resultLines) = testLengths tst
32+
resLine = startLine + exprLines
33+
in ( Range
34+
(Position startLine 0)
35+
--(Position (startLine + exprLines + resultLines) 0),
36+
(Position resLine 0)
37+
, Range (Position resLine 0) (Position (resLine + resultLines) 0)
38+
)
39+
40+
{- |The document range where a test is defined
41+
testRange :: Loc Test -> Range
42+
testRange = fst . testRanges
43+
-}
44+
45+
-- |The document range where the result of the test is defined
46+
resultRange :: Test -> Range
47+
resultRange = snd . testRanges
48+
49+
-- TODO: handle BLANKLINE
50+
{-
51+
>>> showDiffs $ getDiff ["abc","def","ghi","end"] ["abc","def","Z","ZZ","end"]
52+
["abc","def","WAS ghi","NOW Z","NOW ZZ","end"]
53+
-}
54+
showDiffs :: (Semigroup a, IsString a) => [Diff a] -> [a]
55+
showDiffs = map showDiff
56+
57+
showDiff :: (Semigroup a, IsString a) => Diff a -> a
58+
showDiff (First w) = "WAS " <> w
59+
showDiff (Second w) = "NOW " <> w
60+
showDiff (Both w _) = w
61+
62+
testCheck :: Bool -> (Section, Test) -> [T.Text] -> [T.Text]
63+
testCheck diff (section, test) out
64+
| not diff || null (testOutput test) || sectionLanguage section == Plain = out
65+
| otherwise = showDiffs $ getDiff (map T.pack $ testOutput test) out
66+
67+
testLengths :: Test -> (Int, Int)
68+
testLengths (Example e r _) = (NE.length e, length r)
69+
testLengths (Property _ r _) = (1, length r)
70+
71+
-- |A one-line Haskell statement
72+
type Statement = Loc String
73+
74+
asStatements :: Test -> [Statement]
75+
asStatements lt = locate $ Located (fromIntegral $ testRange lt ^. start.line) (asStmts lt)
76+
77+
asStmts :: Test -> [Txt]
78+
asStmts (Example e _ _) = NE.toList e
79+
asStmts (Property t _ _) =
80+
["prop11 = " ++ t, "(propEvaluation prop11 :: IO String)"]
81+
82+
83+
-- |GHC declarations required for expression evaluation
84+
evalSetup :: Ghc ()
85+
evalSetup = do
86+
preludeAsP <- parseImportDecl "import qualified Prelude as P"
87+
context <- getContext
88+
setContext (IIDecl preludeAsP : context)
89+
90+
-- | A wrapper of 'InteractiveEval.execStmt', capturing the execution result
91+
myExecStmt :: String -> ExecOptions -> Ghc (Either String (Maybe String))
92+
myExecStmt stmt opts = do
93+
(temp, purge) <- liftIO newTempFile
94+
evalPrint <- head <$> runDecls ("evalPrint x = P.writeFile "<> show temp <> " (P.show x)")
95+
modifySession $ \hsc -> hsc {hsc_IC = setInteractivePrintName (hsc_IC hsc) evalPrint}
96+
result <- execStmt stmt opts >>= \case
97+
ExecComplete (Left err) _ -> pure $ Left $ show err
98+
ExecComplete (Right _) _ -> liftIO $ Right . (\x -> if null x then Nothing else Just x) <$> readFile' temp
99+
ExecBreak{} -> pure $ Right $ Just "breakpoints are not supported"
100+
liftIO purge
101+
pure result
102+
103+
{- |GHC declarations required to execute test properties
104+
105+
Example:
106+
107+
prop> \(l::[Bool]) -> reverse (reverse l) == l
108+
+++ OK, passed 100 tests.
109+
110+
prop> \(l::[Bool]) -> reverse l == l
111+
*** Failed! Falsified (after 6 tests and 2 shrinks):
112+
[True,False]
113+
-}
114+
propSetup :: [Loc [Char]]
115+
propSetup =
116+
locate0
117+
[ ":set -XScopedTypeVariables -XExplicitForAll"
118+
, "import qualified Test.QuickCheck as Q11"
119+
, "propEvaluation p = Q11.quickCheckWithResult Q11.stdArgs p >>= error . Q11.output" -- uses `error` to get a multi-line display
120+
]

0 commit comments

Comments
 (0)