Skip to content

Commit

Permalink
Hover Support (#25)
Browse files Browse the repository at this point in the history
* WIP: explore contentProposal function

* feat: get type from resolved tokens

* feat: make the first test pass

* feat: make it work end to end

* feat: add markdown fenced code block support

* refactor: clean up unused functions and imports

* fix: run tests before `installDist` task to catch failures early

* refactor: new package layout

- types: contains common types
- lsp4j: contains the lsp4j bindings

The new structure isolates the two language features diagnostics and
hover from each other. In other words, the core class
diagnostic.Diagnositc should never import hover.Hover and vice versa.

* fix: move `index - 1` logic to `PositionLSP4J.fr`

* refactor: extract `tokenToRange` to `Range.fr` and reuse it in
`Diagnostic.fr`

* chore: update frege gradle plugin 2.0.1 -> 4.0.1

This allows to test multiple modules with the `testFrege` command.

* refactor: rename LSPDiagnostic -> DiagnosticLSP and
FregeDiagnosticService -> DiagnosticService

* feat: map compiler hints -> `DiagnosticSeverity.Information` instead of
`DIagnosticSeverity.Hint`

See #22.

* chore: update frege-gradle-plugin to 4.1.0-alpha

As a result, the installDist task compiles all frege modules and tests
the specified frege modules before installing it.

* fix: update workflow to latest gradle plugin v4.1.0-alpha

* chore: update gradle wrapper 7.1 -> 7.4.2
  • Loading branch information
tricktron authored Jul 15, 2022
1 parent 97cdae9 commit e94ca4e
Show file tree
Hide file tree
Showing 27 changed files with 4,634 additions and 1,651 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
uses: actions/checkout@v2
with:
repository: tricktron/frege-gradle-plugin
ref: v2.0.1-alpha
ref: v4.1.0-alpha
path: frege-gradle-plugin
- name: Install Frege Gradle Plugin to MavenLocal
run: |
Expand Down
31 changes: 20 additions & 11 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
plugins {
id 'application'
id 'ch.fhnw.thga.frege' version '2.0.1-alpha'
id 'ch.fhnw.thga.frege' version '4.1.0-alpha'
}

repositories {
Expand All @@ -9,7 +9,7 @@ repositories {

dependencies {
implementation group: 'org.eclipse.lsp4j', name: 'org.eclipse.lsp4j', version: '0.12.0'
frege group: 'org.eclipse.lsp4j', name: 'org.eclipse.lsp4j', version: '0.12.0'
frege group: 'org.eclipse.lsp4j', name: 'org.eclipse.lsp4j', version: '0.12.0'
implementation files(compileFrege.fregeCompilerJar)

}
Expand All @@ -26,24 +26,33 @@ java {

frege
{
version = '3.25.84'
release = '3.25alpha'
version = '3.25.84'
release = '3.25alpha'
mainSourceDir = layout.projectDirectory.dir('src/main/frege')
mainModule = 'ch.fhnw.thga.fregelanguageserver.diagnostic.Diagnostic'
testModules =
[
'ch.fhnw.thga.fregelanguageserver.diagnostic.Diagnostic',
'ch.fhnw.thga.fregelanguageserver.hover.Hover'
]
}

tasks.register('copyCompiledFregeClasses', Copy)
{
from compileFrege.fregeOutputDir
include '**/*.java'
into layout.projectDirectory.dir('src/main/java')
mustRunAfter compileFrege
dependsOn compileFrege
from frege.outputDir
include '**/*.java'
into layout.projectDirectory.dir('src/main/java')
}

compileJava.configure
{
options.encoding = 'UTF-8'
dependsOn compileFrege, copyCompiledFregeClasses
dependsOn copyCompiledFregeClasses
}

test.dependsOn 'testFrege'
check.dependsOn 'testFrege'

tasks.withType(Jar).configureEach
{
dependsOn copyCompiledFregeClasses, check
}
16 changes: 16 additions & 0 deletions doc.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<HTML>
<META http-equiv="Content-Type" content="text/html;charset=utf-8">
<BODY>
<dl class="func">
<dt class="func">
<SPAN CLASS="code"><a name=descending>descending</a> :: <a class="tref" href="./frege/prelude/PreludeBase.html#Ord">Ord</a> 𝖇 =&gt; (𝖆-&gt;𝖇) -&gt; 𝖆 -&gt; 𝖆 -&gt; <a class="tref" href="./frege/prelude/PreludeBase.html#Ordering">Ordering</a></SPAN></dt>
<dd class="func">
<p>
<SPAN CLASS="code">descending f</SPAN> applies a projection function on both sides of <a class="fref" href="./frege/prelude/PreludeBase.html#Ord.$lt$eq$gt">Ord.&lt;=&gt;</a>, but flips arguments. Example usage:</p>
<PRE>
sortBy (descending fst) [(1, "z"), (2, "b")] == [(2, "b"), (1, "z")]</PRE>
</dd>
</dl>
</BODY>
</HTML>
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version = 3.0.0-alpha
version = 3.1.0-alpha
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
module ch.fhnw.thga.fregelanguageserver.diagnostic.Diagnostic where

import ch.fhnw.thga.fregelanguageserver.compiler.CompilerHelper (compileFregeFile, lspGlobal)

import ch.fhnw.thga.fregelanguageserver.types.Position (Position)
import ch.fhnw.thga.fregelanguageserver.types.Range (Range, tokenToRange)
import Compiler.types.Global (StG, StIO, Message, Global, Severity(), tokens, getST, liftStG, liftIO)
import Compiler.types.Tokens (Token)
import frege.compiler.types.Positions (Position TokenPosition)
Expand All @@ -18,22 +19,6 @@ fromCompilerSeverity Severity.ERROR = ERROR
fromCompilerSeverity Severity.WARNING = WARNING
fromCompilerSeverity Severity.HINT = HINT

data Position = Position {
line :: Int, -- 0 based
character :: Int, -- 0 based character offset of a line
}

derive Eq Position
derive Show Position

data Range = Range {
start :: Position,
end :: Position -- exclusive end
}

derive Eq Range
derive Show Range

data Diagnostic = Diagnostic {
range :: Range,
severity :: DiagnosticSeverity,
Expand All @@ -45,33 +30,29 @@ derive Eq Diagnostic
derive Show Diagnostic

tokensToRange :: [ Token ] -> Range
tokensToRange [] = Range { start = Position 0 0, end = Position 1 0 }
tokensToRange [ tk ] = Range
{
start = Position (tk.line - 1) (tk.col - 1),
end = Position (tk.line - 1) ((tk.col - 1) + (length tk.value))
}
tokensToRange [] = Range { start = Position 1 0, end = Position 2 0 }
tokensToRange [ tk ] = tokenToRange tk
tokensToRange tks =
let
startToken = head tks
endToken = last tks
in
Range
{
start = Position (startToken.line - 1) (startToken.col - 1),
end = Position (endToken.line - 1) ((endToken.col - 1) + (length endToken.value))
start = Position startToken.line startToken.col,
end = Position endToken.line (endToken.col + (length endToken.value))
}

createRangeFromPos :: TokenPosition -> StG Range
createRangeFromPos pos = do
gl <- getST
gl <- getST
toks = tokens pos gl
pure $ tokensToRange toks

createDiagnosticFromMessage :: Message -> StG Diagnostic
createDiagnosticFromMessage message = do
range <- createRangeFromPos message.pos
pure $ Diagnostic
pure $ Diagnostic
{
range = range,
severity = fromCompilerSeverity message.level,
Expand All @@ -81,83 +62,83 @@ createDiagnosticFromMessage message = do

extractDiagnostics :: StG [ Diagnostic ]
extractDiagnostics = do
gl <- getST
gl <- getST
diagnostics = fmap createDiagnosticFromMessage gl.sub.messages
sequence diagnostics

compileAndGetDiagnostics :: String -> IO [ Diagnostic ]
compileAndGetDiagnostics fregeCode = do
startGlobal <- lspGlobal
gl <- execStateT (compileFregeFile fregeCode) startGlobal
pure $ evalState (extractDiagnostics) gl
gl <- execStateT (compileFregeFile fregeCode) startGlobal
pure $ evalState (extractDiagnostics) gl

fregeLSPServerShouldMapNoCompilerMessagesToEmptyArray :: Property
fregeLSPServerShouldMapNoCompilerMessagesToEmptyArray = once $ morallyDubiousIOProperty do
fregeCodeWithoutError = "module CorrectFregeTest where\n\n" ++ "ok = 42 + 42"
expected = []
actual <- compileAndGetDiagnostics fregeCodeWithoutError
pure $ expected == actual
expected = []
actual <- compileAndGetDiagnostics fregeCodeWithoutError
pure $ expected == actual

fregeLSPServerShouldMapSingleCompilerMessageToDiagnostics :: Property
fregeLSPServerShouldMapSingleCompilerMessageToDiagnostics = once $ morallyDubiousIOProperty do
fregeCodeWithError = "module ch.fhnw.thga.FaultyFregeTest where\n\nimport Does.not.Exist"
expected =
expected =
[
Diagnostic
{
range = Range { start = Position 2 7, end = Position 2 21 },
range = Range { start = Position 3 8, end = Position 3 22 },
severity = ERROR,
source = "frege compiler",
message = "Could not import module frege.does.not.Exist\n(java.lang.ClassNotFoundException: frege.does.not.Exist)"
source = "frege compiler",
message = "Could not import module frege.does.not.Exist\n(java.lang.ClassNotFoundException: frege.does.not.Exist)"
}
]
gl <- lspGlobal
actual <- compileAndGetDiagnostics fregeCodeWithError
pure $ expected == actual
pure $ expected == actual

fregeLSPServerShouldMapMultipleCompilerMessageToDiagnostics :: Property
fregeLSPServerShouldMapMultipleCompilerMessageToDiagnostics = once $ morallyDubiousIOProperty do
fregeCodeWithErrors = "module ch.fhnw.thga.FaultyFregeTest where\n\nerr1 = do\n x = 42\n\nerr2 = [ 22.0 ] ++ \"42\"\n\nerr3 = 42 + \"42\""
expected =
expected =
[
Diagnostic
{
range = Range { start = Position 5 19, end = Position 5 23 },
range = Range { start = Position 6 20, end = Position 6 24 },
severity = ERROR,
source = "frege compiler",
message = "type error in expression\n\"42\"\ntype is : String\nexpected: [t1]"
source = "frege compiler",
message = "type error in expression\n\"42\"\ntype is : String\nexpected: [t1]"
},
Diagnostic
{
range = Range { start = Position 7 10, end = Position 7 11 },
range = Range { start = Position 8 11, end = Position 8 12 },
severity = ERROR,
source = "frege compiler",
message = "String is not an instance of Num"
source = "frege compiler",
message = "String is not an instance of Num"
},
Diagnostic
{
range = Range { start = Position 2 7, end = Position 2 9 },
range = Range { start = Position 3 8, end = Position 3 10 },
severity = ERROR,
source = "frege compiler",
message = "last statement in a monadic do block must not\nbe let decls"
source = "frege compiler",
message = "last statement in a monadic do block must not\nbe let decls"
}
]
gl <- lspGlobal
actual <- compileAndGetDiagnostics fregeCodeWithErrors
pure $ expected == actual
pure $ expected == actual

posToTokens :: [ TokenPosition ] -> Global -> [ Token ]
posToTokens [] _ = []
posToTokens [] _ = []
posToTokens (p:ps) gl = tokens p gl ++ posToTokens ps gl

main :: IO ()
main = do
lspGlobal <- lspGlobal
let fregeCode = "module ch.fhnw.thga.FaultyFregeTest where\n\nerr1 = do\n x = 42\n\nerr2 = [ 22.0 ] ++ \"42\"\n\nerr3 = 42 + \"42\"\n\n"
lspGlobal <- lspGlobal
let fregeCode = "module ch.fhnw.thga.FaultyFregeTest where\n\nerr1 = do\n x = 42\n\nerr2 = [ 22.0 ] ++ \"42\"\n\nerr3 = 42 + \"42\"\n\n"
let trickyFregeCode = "module FaultyFregeTest where\n\nsimplyString s = s\n\nerr1 = (simplyString 42) ++ \"test\""
gl <- execStateT (compileFregeFile trickyFregeCode) lspGlobal
println $ CharSequence.toString gl.sub.code
gl <- execStateT (compileFregeFile trickyFregeCode) lspGlobal
println $ CharSequence.toString gl.sub.code
for gl.sub.messages println
let positions = map (Message.pos) gl.sub.messages
let toks = posToTokens positions gl
let positions = map (Message.pos) gl.sub.messages
let toks = posToTokens positions gl
for toks println
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
module ch.fhnw.thga.fregelanguageserver.diagnostic.LSPDiagnostic where
module ch.fhnw.thga.fregelanguageserver.diagnostic.DiagnosticLSP where

import ch.fhnw.thga.fregelanguageserver.types.Position (Position)
import ch.fhnw.thga.fregelanguageserver.types.Range (Range)
import ch.fhnw.thga.fregelanguageserver.diagnostic.Diagnostic (
Position, DiagnosticSeverity, Range, Diagnostic, compileAndGetDiagnostics)
DiagnosticSeverity, Diagnostic, compileAndGetDiagnostics)

import ch.fhnw.thga.fregelanguageserver.lsp4j.PositionLSP4J (PositionLSP)
import ch.fhnw.thga.fregelanguageserver.lsp4j.RangeLSP4J (RangeLSP)

data DiagnosticSeverityLSP = pure native org.eclipse.lsp4j.DiagnosticSeverity where
pure native error "org.eclipse.lsp4j.DiagnosticSeverity.Error" :: DiagnosticSeverityLSP
Expand All @@ -10,30 +15,16 @@ data DiagnosticSeverityLSP = pure native org.eclipse.lsp4j.DiagnosticSeverity wh
pure native hint "org.eclipse.lsp4j.DiagnosticSeverity.Hint" :: DiagnosticSeverityLSP

fromCompilerSeverity :: DiagnosticSeverity -> DiagnosticSeverityLSP
fromCompilerSeverity HINT = hint
fromCompilerSeverity HINT = information
fromCompilerSeverity WARNING = warning
fromCompilerSeverity ERROR = error
fromCompilerSeverity INFORMATION = information

data PositionLSP = pure native org.eclipse.lsp4j.Position where
pure native new :: Int -> Int -> PositionLSP

fromPosition :: Position -> PositionLSP
fromPosition pos = new pos.line pos.character

data RangeLSP = pure native org.eclipse.lsp4j.Range where
pure native new :: PositionLSP -> PositionLSP -> RangeLSP

fromRange :: Range -> RangeLSP
fromRange Range { start, end } = new
(PositionLSP.fromPosition start)
(PositionLSP.fromPosition end)

data DiagnosticLSP = pure native org.eclipse.lsp4j.Diagnostic where
pure native new :: RangeLSP -> String -> DiagnosticSeverityLSP -> String -> DiagnosticLSP

fromDiagnostic :: Diagnostic -> DiagnosticLSP
fromDiagnostic Diagnostic { range, message, severity, source } =
fromDiagnostic Diagnostic { range,severity, source, message } =
new
(RangeLSP.fromRange range)
message
Expand All @@ -52,6 +43,6 @@ data ArrayList a = native java.util.ArrayList where

compileAndGetDiagnosticsLSP :: String -> IOMutable (ArrayList DiagnosticLSP)
compileAndGetDiagnosticsLSP fregeCode = do
diagnostics <- compileAndGetDiagnostics fregeCode
diagnostics <- compileAndGetDiagnostics fregeCode
diagnosticsLSP = DiagnosticLSP.fromDiagnostic <$> diagnostics
ArrayList.fromFregeList diagnosticsLSP
Loading

0 comments on commit e94ca4e

Please sign in to comment.