elm-tooling / elm-language-server-haskell

Elm language server written in haskell (archived). Use https://github.com/elm-tooling/elm-language-server instead.
BSD 3-Clause "New" or "Revised" License
52 stars 5 forks source link

Feature: Go to defintion #10

Open andys8 opened 5 years ago

andys8 commented 5 years ago

Feature

Based on the selection the user should be able to jump to the file to the position where the selection is defined. This can be in a dependency or the codebase of the application.

Gathering information

Elm Compiler

Interfaces are defined in Elm.Interface.

type Interfaces =
  Map.Map ModuleName.Canonical Interface

data Interface =
  Interface
    { _types   :: Map.Map N.Name Can.Annotation
    , _unions  :: Map.Map N.Name Union
    , _aliases :: Map.Map N.Name Alias
    , _binops  :: Map.Map N.Name Binop
    }

https://github.com/matheus23/elm-compiler-library/blob/502c59df4ac3df6578f99d3eb789d3eb96e3b5a0/compiler/src/Elm/Interface.hs#L34-L48

Stuff.Verify.verify emits Summary and is already used. Defined in Elm.Project.Summary.


data Summary =
  Summary
    { _root :: FilePath
    , _project :: Project
    , _exposed :: ExposedModules
    , _ifaces :: Module.Interfaces
    , _depsGraph :: DepsGraph
    }

type ExposedModules =
  Map.Map Module.Raw [Package]

type DepsGraph =
  Map.Map Name ( Version, [Name] )

https://github.com/matheus23/elm-compiler-library/blob/502c59df4ac3df6578f99d3eb789d3eb96e3b5a0/builder/src/Elm/Project/Summary.hs#L27-L42

File.Find.find can return the path of the file on disk, given Summary, Origin and Module.Raw.

data Asset
  = Local FilePath
  | Kernel FilePath (Maybe FilePath)
  | Foreign Pkg.Package
  | ForeignKernel

find :: Summary.Summary -> E.Origin -> Module.Raw -> Task.Task_ E.Problem Asset
find (Summary.Summary root project exposed _ _) origin name =
  do  here <- liftIO Dir.getCurrentDirectory
      let toRoot dir = FP.makeRelative here (root </> dir)
      case project of
        Project.App info ->
          do  let srcDirs = map toRoot (Project._app_source_dirs info)
              findElm project srcDirs exposed origin name

        Project.Pkg _ ->
          if N.startsWith "Elm.Kernel." name then
            if Project.isPlatformPackage project then
              findKernel (toRoot "src") exposed origin name
            else
              Task.throw $ E.ModuleNameReservedForKernel origin name

          else
            findElm project [ toRoot "src" ] exposed origin name

https://github.com/matheus23/elm-compiler-library/blob/502c59df4ac3df6578f99d3eb789d3eb96e3b5a0/builder/src/File/Find.hs#L30-L58

Language Server Protocol

Called "Goto Type Definition Request".

Request: method: ‘textDocument/definition’ params: TextDocumentPositionParams

Response: result: Location | Location[] | null

https://microsoft.github.io/language-server-protocol/specification#textDocument_definition

Related: Goto Type Definition Request, Goto Implementation Request

Haskell LSP

type DefinitionRequest = 
  RequestMessage ClientMethod TextDocumentPositionParams LocationResponseParams

type DefinitionResponse = 
  ResponseMessage LocationResponseParams

https://hackage.haskell.org/package/haskell-lsp-types-0.8.0.1/docs/Language-Haskell-LSP-Types.html#t:DefinitionRequest

Comparison

intellij-elm

The klazuka/intellij-elm (elm plugin for IntelliJ in Kotlin) has the go to source feature. https://github.com/klazuka/intellij-elm/blob/master/src/main/kotlin/org/elm/ide/navigation/ElmGoToSymbolContributor.kt

andys8 commented 5 years ago

This is a dump of interface representations for a sample package elm-geohash created by deriving Show instances: https://pastebin.com/raw/6GeLnsGj

mbylstra commented 5 years ago

Hi there. I'm working on an experimental project where I want access to the type definitions generated from the compiler. I arrived at a similar discovery, that Elm.Interface has the goods, and deriving Show can be used to dump contents of a real package. I found http://hackage.haskell.org/package/show-prettyprint useful for a more readable output.

andys8 commented 5 years ago

@mbylstra Oh, thanks! I even know the author of the package.

https://pastebin.com/raw/dbJj8wWv

andys8 commented 5 years ago

A dump of Summary

Note: There is a part missing in the middle because of file limits.

https://gist.github.com/andys8/d73996fa4739a179406a19da9e08c43f

andys8 commented 5 years ago

Compare with https://github.com/stoeffel/elmi-to-json

andys8 commented 5 years ago

@matheus23 documented the elm-compiler architecture: https://github.com/matheus23/elm-compiler-library/blob/502c59df4ac3df6578f99d3eb789d3eb96e3b5a0/COMPILER.md

andys8 commented 5 years ago

Compile.runTypeInference returns a Map with Annotations. The result contains more information than exposed modules.

runTypeInference :: L.Localizer -> Can.Module -> Result i (Map.Map N.Name Can.Annotation)
runTypeInference localizer canonical =
  case unsafePerformIO (Type.run =<< Type.constrain canonical) of
    Right annotations ->
      Result.ok annotations

    Left errors ->
      Result.throw (Error.Type localizer errors)

https://github.com/matheus23/elm-compiler-library/blob/502c59df4ac3df6578f99d3eb789d3eb96e3b5a0/compiler/src/Compile.hs#L92-L99

Example

Dump: https://gist.github.com/andys8/3ab81b72fee326df726779c532bf14f8

flipTuple : a -> b -> ( b, a )
flipTuple a b =
    ( b, a )
(Name {_name = "flipTuple"}
         ,Forall (fromList [(Name {_name = "a"},()),(Name {_name = "b"},())])
                 (TLambda (TVar (Name {_name = "a"}))
                          (TLambda (TVar (Name {_name = "b"}))
                                   (TTuple (TVar (Name {_name = "b"}))
                                           (TVar (Name {_name = "a"}))
                                           Nothing))))