Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft: Implement a language server #100

Closed
wants to merge 77 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
4e005d1
Gitignore .vscode
fwcd Dec 8, 2020
c3db897
Add language server mode
fwcd Dec 8, 2020
5d5b075
Add language server protocol library
fwcd Dec 8, 2020
1889536
Add dependency on lens library
fwcd Dec 8, 2020
665f959
Update stack resolver
fwcd Dec 8, 2020
98cde17
Add unliftio extra-dep
fwcd Dec 8, 2020
d86f6b6
Implement basic LSP handler
fwcd Dec 8, 2020
19f0d18
Move 'fail' implementations to MonadFail
fwcd Dec 8, 2020
206d174
Implement 'fail' for IOErr
fwcd Dec 8, 2020
479718e
Implement 'fail' for Asm
fwcd Dec 8, 2020
e62a764
Add option for language server mode
fwcd Dec 8, 2020
2ea5c22
Add language client to VSCode extension
fwcd Dec 8, 2020
90d0aa0
Fix VSCode extension executable path
fwcd Dec 8, 2020
ed510ef
Add missing options to language server runner
fwcd Dec 8, 2020
371dd04
Implement experimental hover
fwcd Dec 8, 2020
e42d151
Control range map generation via new flag
fwcd Dec 8, 2020
e420c6b
Derive Show on RangeMap
fwcd Dec 8, 2020
c3f8dee
Implement hover using range map
fwcd Dec 8, 2020
4618e99
Use actual range in hover result
fwcd Dec 8, 2020
e86f8e7
Add rangeLength
fwcd Dec 8, 2020
20d5001
Find innermost range in rangeMapFindAt
fwcd Dec 8, 2020
f79f53b
Fix off-by-ones in LSP pos/range conversions
fwcd Dec 8, 2020
fc98530
Only start language client in VSCode extension if option is set
fwcd Dec 8, 2020
de44cb8
Improve todo comments
fwcd Dec 8, 2020
cbc8747
Update cabal file
fwcd Dec 9, 2020
a085ae1
Update language server options in VSCode extension
fwcd Dec 9, 2020
ae607d9
Perform some stylistic cleanup in LanguageServer.Handlers
fwcd Dec 9, 2020
0d1c20e
Using pretty-printer to generate better hovers
fwcd Dec 9, 2020
54d2e19
Factor out pos/range LSP conversions into separate module
fwcd Dec 9, 2020
f22e5cf
Move hover into separate module
fwcd Dec 9, 2020
39038ae
Improve handling of errors when creating hover
fwcd Dec 9, 2020
e9225d2
Add conversions for LSP diagnostics
fwcd Dec 9, 2020
d6362a8
Generate experimental LSP diagnostics
fwcd Dec 9, 2020
91c4604
Add source to LSP diagnostics and flush them
fwcd Dec 9, 2020
f2f5d41
Clean up imports in hover module
fwcd Dec 9, 2020
74f144d
Move hover handler
fwcd Dec 10, 2020
2422049
Add stub handlers for text document changes
fwcd Dec 10, 2020
1436685
Add LSP initialization handler
fwcd Dec 10, 2020
9b89384
Add custom monad for storing language server-specific state
fwcd Dec 10, 2020
7048e96
Add accessors to language server monad
fwcd Dec 10, 2020
2af326f
WIP: Recompile text documents in language server on changes
fwcd Dec 10, 2020
ae48beb
Enable open/close notifications
fwcd Dec 10, 2020
8353451
Share the LSState among all handlers
fwcd Dec 10, 2020
eb21f36
Fix LS state accessor
fwcd Dec 10, 2020
b37bf3b
Remove debug log messages from LSP text doc handlers
fwcd Dec 10, 2020
c646824
Add LSP location conversions
fwcd Dec 11, 2020
ae2851b
Add LSP definition handler
fwcd Dec 11, 2020
d31c02a
Remove unused 'rightToMaybe' function
fwcd Dec 11, 2020
bc0a6ea
Look up constructor definitions in LSP definition handler
fwcd Dec 11, 2020
aa604a1
Look up types in LSP definition handler
fwcd Dec 11, 2020
0c47ef1
Look up synonyms in LSP definition handler
fwcd Dec 11, 2020
317fe77
Clean up recompileFile function in LSP handler
fwcd Dec 11, 2020
117ba29
Add unneccessary tag to warnings mentioning 'unused'
fwcd Dec 11, 2020
3338b7f
Add some more doc comments
fwcd Dec 11, 2020
0c11297
Add document symbol LSP handler
fwcd Dec 11, 2020
88a7c29
Update imports in LSP handlers
fwcd Dec 11, 2020
97c84f5
List top-level declarations as document symbols
fwcd Dec 11, 2020
9381e70
Traverse expressions and patterns for symbols
fwcd Dec 11, 2020
06e4655
Extract symbols from handlers and lambda expressions
fwcd Dec 11, 2020
9bc5fae
Extract symbols from type declarations
fwcd Dec 11, 2020
9533e32
Add HasSymbols instance over lists
fwcd Dec 11, 2020
c436f3c
Provide richer LSP symbol kinds on type declarations
fwcd Dec 11, 2020
810a966
Switch 'vscode' dependency to '@types/vscode'
fwcd Dec 13, 2020
369b33e
Update description of the VSCode extension
fwcd Dec 13, 2020
68e4db8
Merge branch 'dev' into language-server
fwcd Dec 13, 2020
c78fb45
Add completion handler stub
fwcd Dec 13, 2020
9234ac5
Add basic value completions
fwcd Dec 13, 2020
2eb4340
Add Kind.Constructors.constructorsList
fwcd Dec 13, 2020
2184bc7
Add basic constructor completions
fwcd Dec 13, 2020
a7d6a85
Add basic synonym completions
fwcd Dec 13, 2020
49aa566
Update note on type completions
fwcd Dec 13, 2020
4d474a1
Generate more specific completion item kinds
fwcd Dec 13, 2020
a503d27
Provide types in completion detail
fwcd Dec 13, 2020
a54b90d
Add some todo notes regarding code completion enhancements
fwcd Dec 13, 2020
8e10a56
Update vscode dependencies
fwcd Dec 14, 2020
93d10b0
Fix vscode-languageclient imports
fwcd Dec 14, 2020
6dfee8f
Add keyword completions
fwcd Dec 15, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
.trash/
.idea/
.vs/
.vscode/
node_modules/
out/
dist/
Expand Down
18 changes: 16 additions & 2 deletions koka.cabal
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
cabal-version: 1.12

-- This file has been generated from package.yaml by hpack version 0.33.0.
-- This file has been generated from package.yaml by hpack version 0.31.2.
--
-- see: https://github.com/sol/hpack
--
-- hash: 5566036dac3bd0ad45754486154ddd84027b16d81b584e00838c079caf6268eb
-- hash: 05735bf23830ba3e79a733b1af1ce1de608c17203e466c1377e589caac3a0a3d

name: koka
version: 2.0.15
Expand Down Expand Up @@ -88,6 +88,16 @@ executable koka
Kind.Pretty
Kind.Synonym
Kind.Unify
LanguageServer.Conversions
LanguageServer.Handler.Completion
LanguageServer.Handler.Definition
LanguageServer.Handler.DocumentSymbol
LanguageServer.Handler.Hover
LanguageServer.Handler.Initialized
LanguageServer.Handler.TextDocument
LanguageServer.Handlers
LanguageServer.Monad
LanguageServer.Run
Lib.JSON
Lib.PPrint
Lib.Printer
Expand Down Expand Up @@ -142,6 +152,8 @@ executable koka
, containers
, directory
, haskeline
, lens
, lsp
, mtl
, parsec
, process
Expand Down Expand Up @@ -173,6 +185,8 @@ test-suite koka-test
, haskeline
, hspec
, hspec-core
, lens
, lsp
, mtl
, parsec
, process
Expand Down
2 changes: 2 additions & 0 deletions package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ dependencies:
- cond
- containers
- directory
- lens
- lsp
- mtl
- parsec
- process
Expand Down
3 changes: 3 additions & 0 deletions src/Backend/C/FromCore.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2026,6 +2026,9 @@ instance Monad Asm where
(x,st1) -> case f x of
Asm b -> b env st1)

instance MonadFail Asm where
fail = failure

runAsm :: Int -> Env -> Asm a -> (a,Doc,Doc)
runAsm uniq initEnv (Asm asm)
= case asm initEnv (initSt uniq) of
Expand Down
6 changes: 5 additions & 1 deletion src/Common/Range.hs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ module Common.Range
( Pos, makePos, minPos, maxPos, posColumn, posLine
, posMove8, posMoves8, posNull
, Range, showFullRange
, makeRange, rangeNull, combineRange, rangeEnd, rangeStart
, makeRange, rangeNull, combineRange, rangeEnd, rangeStart, rangeLength
, Ranged( getRange ), combineRanged
, combineRangeds, combineRanges, extendRange
, Source(Source,sourceName, sourceBString), sourceText, sourceFromRange
Expand Down Expand Up @@ -271,6 +271,10 @@ rangeStart (Range p1 p2) = p1
rangeEnd :: Range -> Pos
rangeEnd (Range p1 p2) = p2

-- | Return the length of a range
rangeLength :: Range -> Int
rangeLength (Range p1 p2) = posOfs p2 - posOfs p1

-- | Return the source of a range
rangeSource :: Range -> Source
rangeSource = posSource . rangeStart
Expand Down
3 changes: 1 addition & 2 deletions src/Compiler/Compile.hs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ instance Monad IOErr where
instance MonadFail IOErr where
fail = liftError . fail


bindIO :: IO (Error a) -> (a -> IO (Error b)) -> IO (Error b)
bindIO io f
= do err <- io
Expand Down Expand Up @@ -808,7 +807,7 @@ inferCheck loaded flags line coreImports program1
(isValueFromFlags flags)
(colorSchemeFromFlags flags)
(platform flags)
(if (outHtml flags > 0) then Just rangeMapNew else Nothing)
(if genRangeMap flags then Just rangeMapNew else Nothing)
(loadedImportMap loaded)
(loadedKGamma loaded)
(loadedSynonyms loaded)
Expand Down
16 changes: 13 additions & 3 deletions src/Compiler/Options.hs
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,13 @@ prettyIncludePath flags
data Mode
= ModeHelp
| ModeVersion
| ModeCompiler { files :: [FilePath] }
| ModeInteractive { files :: [FilePath] }
| ModeCompiler { files :: [FilePath] }
| ModeInteractive { files :: [FilePath] }
| ModeLanguageServer { files :: [FilePath] }

data Option
= Interactive
| LanguageServer
| Version
| Help
| Flag (Flags -> Flags)
Expand Down Expand Up @@ -133,6 +135,7 @@ data Flags
, coreCheck :: Bool
, enableMon :: Bool
, semiInsert :: Bool
, genRangeMap :: Bool
, localBinDir :: FilePath -- directory of koka executable
, localDir :: FilePath -- install prefix: /usr/local
, localLibDir :: FilePath -- precompiled object files: <prefix>/lib/koka/v2.x.x /<cc>-<config>/libkklib.a, /<cc>-<config>/std_core.kki, ...
Expand Down Expand Up @@ -198,6 +201,7 @@ flagsNull
False -- coreCheck
True -- enableMonadic
True -- semi colon insertion
False -- generate range map
"" -- koka executable dir
"" -- prefix dir (default: <program-dir>/..)
"" -- localLib dir
Expand All @@ -223,6 +227,9 @@ isVersion _ = False
isInteractive Interactive = True
isInteractive _ = False

isLanguageServer LanguageServer = True
isLanguageServer _ = False

isValueFromFlags flags
= dataInfoIsValue

Expand All @@ -239,6 +246,7 @@ options = (\(xss,yss) -> (concat xss, concat yss)) $ unzip
[ option ['?','h'] ["help"] (NoArg Help) "show this information"
, option [] ["version"] (NoArg Version) "show the compiler version"
, option ['p'] ["prompt"] (NoArg Interactive) "interactive mode"
, option [] ["language-server"] (NoArg LanguageServer) "language server mode"
, flag ['e'] ["execute"] (\b f -> f{evaluate= b}) "compile and execute (default)"
, flag ['c'] ["compile"] (\b f -> f{evaluate= not b}) "only compile, do not execute"
, option ['i'] ["include"] (OptArg includePathFlag "dirs") "add <dirs> to search path (empty resets)"
Expand Down Expand Up @@ -464,6 +472,7 @@ processOptions flags0 opts
mode = if (any isHelp options) then ModeHelp
else if (any isVersion options) then ModeVersion
else if (any isInteractive options) then ModeInteractive files
else if (any isLanguageServer options) then ModeLanguageServer files
else if (null files) then ModeInteractive files
else ModeCompiler files
in do ed <- if (null (editor flags))
Expand All @@ -483,7 +492,8 @@ processOptions flags0 opts
ccompPath = ccmd,
ccomp = cc,
editor = ed,
includePath = (localShareDir ++ "/lib") : includePath flags }
includePath = (localShareDir ++ "/lib") : includePath flags,
genRangeMap = outHtml flags > 0 || any isLanguageServer options }
,mode)
else invokeError errs

Expand Down
6 changes: 5 additions & 1 deletion src/Kind/Constructors.hs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module Kind.Constructors( -- * Constructors
, constructorsExtend, constructorsLookup, constructorsFind
, constructorsIsEmpty
, constructorsFindScheme
, constructorsSet
, constructorsSet, constructorsList
, constructorsCompose, constructorsFromList
, extractConstructors
-- * Pretty
Expand Down Expand Up @@ -78,6 +78,10 @@ constructorsSet :: Constructors -> S.NameSet
constructorsSet (Constructors m)
= S.fromList (M.keys m)

constructorsList :: Constructors -> [(Name, ConInfo)]
constructorsList (Constructors m)
= M.toList m

{--------------------------------------------------------------------------
Pretty printing
--------------------------------------------------------------------------}
Expand Down
73 changes: 73 additions & 0 deletions src/LanguageServer/Conversions.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
-----------------------------------------------------------------------------
-- Conversions between LSP types and internal types, e.g. positions/ranges
-----------------------------------------------------------------------------
{-# LANGUAGE OverloadedStrings #-}
module LanguageServer.Conversions( -- * Conversions to LSP types
toLspPos, toLspRange, toLspLocation
, toLspDiagnostics, toLspErrorDiagnostics, toLspWarningDiagnostic
-- * Conversions from LSP types
, fromLspPos, fromLspRange, fromLspLocation ) where

import qualified Common.Error as E
import qualified Common.Range as R
import qualified Data.Text as T
import qualified Language.LSP.Types as J
import Lib.PPrint ( Doc )

toLspPos :: R.Pos -> J.Position
toLspPos p = J.Position (R.posLine p - 1) (R.posColumn p - 1) -- LSP positions are zero-based

toLspRange :: R.Range -> J.Range
toLspRange r = J.Range (J.Position l1 c1) (J.Position l2 $ c2 + 1) -- LSP range ends are exclusive
where
J.Position l1 c1 = toLspPos $ R.rangeStart r
J.Position l2 c2 = toLspPos $ R.rangeEnd r

toLspLocation :: R.Range -> J.Location
toLspLocation r = J.Location uri (toLspRange r)
where
uri = J.filePathToUri $ R.sourceName $ R.rangeSource r

toLspDiagnostics :: J.DiagnosticSource -> E.Error a -> [J.Diagnostic]
toLspDiagnostics src err = case E.checkError err of
Right (_, ws) -> map (uncurry $ toLspWarningDiagnostic src) ws
Left e -> toLspErrorDiagnostics src e

toLspErrorDiagnostics :: J.DiagnosticSource -> E.ErrorMessage -> [J.Diagnostic]
toLspErrorDiagnostics src e = case e of
E.ErrorGeneral r doc -> [makeDiagnostic J.DsError src r doc]
E.ErrorParse r doc -> [makeDiagnostic J.DsError src r doc]
E.ErrorStatic rds -> map (uncurry $ makeDiagnostic J.DsError src) rds
E.ErrorKind rds -> map (uncurry $ makeDiagnostic J.DsError src) rds
E.ErrorType rds -> map (uncurry $ makeDiagnostic J.DsError src) rds
E.ErrorWarning rds e' -> map (uncurry $ makeDiagnostic J.DsError src) rds ++ toLspErrorDiagnostics src e'
E.ErrorIO doc -> [makeDiagnostic J.DsError src R.rangeNull doc]
E.ErrorZero -> []

toLspWarningDiagnostic :: J.DiagnosticSource -> R.Range -> Doc -> J.Diagnostic
toLspWarningDiagnostic = makeDiagnostic J.DsWarning

makeDiagnostic :: J.DiagnosticSeverity -> J.DiagnosticSource -> R.Range -> Doc -> J.Diagnostic
makeDiagnostic s src r doc = J.Diagnostic range severity code source message tags related
where
range = toLspRange r
severity = Just s
code = Nothing
source = Just src
message = T.pack $ show doc
tags | "is unused" `T.isInfixOf` message = Just $ J.List [J.DtUnnecessary]
| otherwise = Nothing
related = Nothing

fromLspPos :: J.Uri -> J.Position -> R.Pos
fromLspPos uri (J.Position l c) = R.makePos src (-1) (l + 1) (c + 1)
where
src = case J.uriToFilePath uri of
Just filePath -> R.Source filePath R.bstringEmpty -- TODO: Read file here (and compute the offset correctly)
Nothing -> R.sourceNull

fromLspRange :: J.Uri -> J.Range -> R.Range
fromLspRange uri (J.Range s e) = R.makeRange (fromLspPos uri s) (fromLspPos uri e)

fromLspLocation :: J.Location -> R.Range
fromLspLocation (J.Location uri rng) = fromLspRange uri rng
Loading