Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
3432c5b
Initial rough draft of hover functionality
agg23 Oct 9, 2020
8c57afe
Node traversal attempting to find closest to cursor
agg23 Oct 9, 2020
6976b00
Added basic hover symbol formatting
agg23 Oct 9, 2020
34576ab
Add declaration finding
agg23 Oct 10, 2020
86198ab
Go to definition with weird positioning issues
agg23 Oct 10, 2020
6c8a490
Fixed column indexes being off
agg23 Oct 10, 2020
bb641fe
Proper positioning for go to def
agg23 Oct 10, 2020
0afbf4d
Logger stub
agg23 Oct 10, 2020
cc3d58a
Removed accidentally committed config changes
agg23 Oct 24, 2020
3b76bb9
Add -lsp flag to launch language server
agg23 Oct 24, 2020
a7c9594
Basic AST cache
agg23 Oct 24, 2020
4dbcc6d
Module URI logic and partially working GoToDef
agg23 Oct 25, 2020
15f9659
Prebuild AST on document load
agg23 Oct 25, 2020
37b18ce
AST rebuilding on didChange events
agg23 Oct 25, 2020
ca81506
Position and GoToDef for import statements
agg23 Oct 25, 2020
974167d
Find all references support
agg23 Oct 25, 2020
3cd97ec
Added check for isIncludeDeclaration on references
agg23 Oct 25, 2020
f448a37
Merge branch 'SBTIncludeTestFlags' into lsp-experiments
agg23 Oct 30, 2020
ed4c88c
Implemented .millforkrc.json config file
agg23 Oct 31, 2020
d34c480
Proper formatting for asm functions
agg23 Oct 31, 2020
5bff531
Added array formatting
agg23 Oct 31, 2020
47a0a54
Added alias support
agg23 Oct 31, 2020
cfbb6d7
Fixed AST not being built starting from initial paths
agg23 Oct 31, 2020
40ce180
Support for finding local variable declarations
agg23 Nov 1, 2020
e40a810
Search enclosing scopes when finding declarations
agg23 Nov 1, 2020
0ee3320
Fixed hover not working on arrays
agg23 Nov 1, 2020
26f196b
Fixed GoToDef on first line/column of file
agg23 Nov 1, 2020
af36258
Merge branch 'SBTIncludeTestFlags' into lsp-experiments
agg23 Nov 1, 2020
1cf1c29
Added first LSP test
agg23 Nov 1, 2020
fd0ccfe
Expanded initial hover tests
agg23 Nov 1, 2020
0951f49
Test checking every character of file for hoverability
agg23 Nov 2, 2020
23af333
Added basic NodeFinder nodeAtPosition tests
agg23 Nov 2, 2020
c3f8515
Improved findNodeAtPosition to consider the entire flattened tree
agg23 Nov 3, 2020
097859d
Added test for finding function arguments
agg23 Nov 3, 2020
eb274f8
Documented NodeFinder functions
agg23 Nov 3, 2020
751138d
More dynamic mechanism for testing NodeFinder
agg23 Nov 3, 2020
6967158
Documented future test cases and unimplemented functionality
agg23 Nov 3, 2020
54f4b31
Merge branch 'master' of https://github.com/KarolS/millfork into lsp-…
agg23 Nov 3, 2020
b78c7bd
Indexed expression support
agg23 Nov 4, 2020
8d20499
Added test for sums
agg23 Nov 4, 2020
d181e16
Added find references support to arrays and params
agg23 Nov 9, 2020
faf31a8
Fixed find references only returning one result
agg23 Nov 9, 2020
f0db3c1
Rough highlighting of go to def and references (based on name length)
agg23 Nov 9, 2020
0335bb9
Naively sort find references locations
agg23 Nov 9, 2020
a1ffbf3
Fixed crash on failure to find module
agg23 Nov 12, 2020
811449a
Fixed crash on expressions without positions
agg23 Nov 12, 2020
dcf06cb
Updated launch task
agg23 Nov 12, 2020
4c69c54
Switched logging to write to stderr
agg23 Nov 14, 2020
892a90d
Added basic doccomment support to functions
agg23 Nov 14, 2020
162954f
Added markdown formating for @param and @returns
agg23 Nov 14, 2020
8ff5e9c
Added variable and array declaration docstring support
agg23 Nov 14, 2020
8cc4886
Support multiline comments
agg23 Nov 14, 2020
711b149
Fixed module path not being added when loaded through LSP
agg23 Nov 16, 2020
718f47f
Switch to stderr logging only once LSP starts
agg23 Dec 31, 2020
a05a1eb
Enabled other instruction support for LSP
agg23 Dec 31, 2020
7c0551d
Merge branch 'master' of https://github.com/KarolS/millfork into lsp-…
agg23 Dec 31, 2020
75e4f26
Updated build.sbt
agg23 Dec 31, 2020
cd2ab17
Merge branch 'agg23/RemovedOptimizationHintPrint' into lsp-experiments
agg23 Jan 1, 2021
b1f7a05
Fixed paths not being properly sanitized on Windows
agg23 Jan 30, 2021
04fb6fb
Merge branch 'master' of https://github.com/KarolS/millfork into lsp-…
agg23 Feb 3, 2021
f12b734
Merge branch 'lsp-experiments' of https://github.com/agg23/millfork i…
agg23 Feb 3, 2021
1f1e123
Merge branch 'agg23/OverflowErrors' into lsp-experiments
agg23 Feb 3, 2021
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# various directories
target/
.bloop/
.bsp/
.idea/
.metals/
project/target
project/project/target/
stuff
Expand All @@ -14,6 +17,7 @@ include-*/

# hidden files
*.~
.DS_Store

#tools
*.bat
Expand Down
1 change: 1 addition & 0 deletions .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
version = "2.6.4"
18 changes: 18 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
Comment thread
agg23 marked this conversation as resolved.
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "scala",
"name": "Debug",
"request": "launch",
"mainClass": "millfork.Main",
// optional jvm properties to use
"jvmOptions": [],
"args": []
},

]
}
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"files.watcherExclude": {
"**/target": true
}
}
17 changes: 17 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Compile Millfork",
"type": "shell",
"command": "sbt -DskipTests=true compile && sbt -DskipTests=true assembly",
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}
40 changes: 27 additions & 13 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ libraryDependencies += "com.lihaoyi" %% "fastparse" % "1.0.0"

libraryDependencies += "org.apache.commons" % "commons-configuration2" % "2.2"

libraryDependencies += "org.eclipse.lsp4j" % "org.eclipse.lsp4j" % "0.9.0"

libraryDependencies += "net.liftweb" %% "lift-json" % "3.4.2"

libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.8" % "test"

val testDependencies = Seq(
Expand All @@ -33,7 +37,7 @@ val testDependencies = Seq(

val includesTests = System.getProperty("skipTests") == null

libraryDependencies ++=(
libraryDependencies ++= (
if (includesTests) {
println("Including test dependencies")
testDependencies
Expand All @@ -43,26 +47,27 @@ libraryDependencies ++=(
)

(if (!includesTests) {
// Disable assembling tests
sbt.internals.DslEntry.fromSettingsDef(test in assembly := {})
} else {
sbt.internals.DslEntry.fromSettingsDef(Seq[sbt.Def.Setting[_]]())
})
// Disable assembling tests
sbt.internal.DslEntry.fromSettingsDef(test in assembly := {})
} else {
sbt.internal.DslEntry.fromSettingsDef(Seq[sbt.Def.Setting[_]]())
})

mainClass in Compile := Some("millfork.Main")

assemblyJarName := "millfork.jar"

lazy val root = (project in file(".")).
enablePlugins(BuildInfoPlugin).
settings(
lazy val root = (project in file("."))
.enablePlugins(BuildInfoPlugin)
.settings(
buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion),
buildInfoPackage := "millfork.buildinfo"
)

import sbtassembly.AssemblyKeys

val releaseDist = TaskKey[File]("release-dist", "Creates a distributable zip file.")
val releaseDist =
TaskKey[File]("release-dist", "Creates a distributable zip file.")

releaseDist := {
val jar = AssemblyKeys.assembly.value
Expand All @@ -79,7 +84,10 @@ releaseDist := {
IO.createDirectory(distDir)
IO.copyFile(jar, distDir / jar.name)
IO.copyFile(base / "LICENSE", distDir / "LICENSE")
IO.copyFile(base / "src/3rd-party-licenses.txt", distDir / "3rd-party-licenses.txt")
IO.copyFile(
base / "src/3rd-party-licenses.txt",
distDir / "3rd-party-licenses.txt"
)
IO.copyFile(base / "CHANGELOG.md", distDir / "CHANGELOG.md")
IO.copyFile(base / "README.md", distDir / "README.md")
IO.copyFile(base / "COMPILING.md", distDir / "COMPILING.md")
Expand All @@ -89,8 +97,14 @@ releaseDist := {
}
copyDir("include")
copyDir("docs")
def entries(f: File): List[File] = f :: (if (f.isDirectory) IO.listFiles(f).toList.flatMap(entries) else Nil)
IO.zip(entries(distDir).map(d => (d, d.getAbsolutePath.substring(distDir.getParent.length + 1))), zipFile)
def entries(f: File): List[File] =
f :: (if (f.isDirectory) IO.listFiles(f).toList.flatMap(entries) else Nil)
IO.zip(
entries(distDir).map(d =>
(d, d.getAbsolutePath.substring(distDir.getParent.length + 1))
),
zipFile
)
IO.delete(distDir)
zipFile
}
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version = 0.13.18
sbt.version = 1.4.0
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any idea what this changes? I was having issues getting my tooling to work due to mixed versions of Scala, so I upgraded SBT.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess a newer SBT shouldn't hurt, as long as it works.

4 changes: 4 additions & 0 deletions project/metals.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// DO NOT EDIT! This file is auto-generated.
// This file enables sbt-bloop to create bloop config files.

addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.4.4-13-408f4d80")
4 changes: 4 additions & 0 deletions project/project/metals.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// DO NOT EDIT! This file is auto-generated.
// This file enables sbt-bloop to create bloop config files.

addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.4.4-13-408f4d80")
4 changes: 4 additions & 0 deletions project/project/project/metals.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// DO NOT EDIT! This file is auto-generated.
// This file enables sbt-bloop to create bloop config files.

addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.4.4-13-408f4d80")
4 changes: 3 additions & 1 deletion src/main/scala/millfork/Context.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import millfork.error.Logger
case class Context(errorReporting: Logger,
inputFileNames: List[String],
outputFileName: Option[String] = None,
configFilePath: Option[String] = None,
runFileName: Option[String] = None,
runParams: Seq[String] = Vector(),
optimizationLevel: Option[Int] = None,
Expand All @@ -21,7 +22,8 @@ case class Context(errorReporting: Logger,
extraIncludePath: Seq[String] = IndexedSeq(),
flags: Map[CompilationFlag.Value, Boolean] = Map(),
features: Map[String, Long] = Map(),
verbosity: Option[Int] = None) {
verbosity: Option[Int] = None,
languageServer: Boolean = false) {
def changeFlag(f: CompilationFlag.Value, b: Boolean): Context = {
if (flags.contains(f)) {
if (flags(f) != b) {
Expand Down
53 changes: 43 additions & 10 deletions src/main/scala/millfork/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,15 @@ import millfork.node.StandardCallGraph
import millfork.output._
import millfork.parser.{MSourceLoadingQueue, MosSourceLoadingQueue, TextCodecRepository, ZSourceLoadingQueue}

import millfork.language.{MfLanguageServer,MfLanguageClient,LanguageServerLogger}
import org.eclipse.lsp4j.services.LanguageServer
import org.eclipse.lsp4j.jsonrpc.Launcher
import java.util.concurrent.Executors
import java.io.PrintWriter
import millfork.cli.JsonConfigParser


object Main {


def main(args: Array[String]): Unit = {
val errorReporting = new ConsoleLogger
implicit val __implicitLogger: Logger = errorReporting
Expand All @@ -34,15 +38,16 @@ object Main {

val startTime = System.nanoTime()
val (status, c0) = parser(errorReporting).parse(Context(errorReporting, Nil), args.toList)
val c1 = JsonConfigParser.parseConfig(c0, errorReporting)
status match {
case CliStatus.Quit => return
case CliStatus.Failed =>
errorReporting.fatalQuit("Invalid command line")
case CliStatus.Ok => ()
}
errorReporting.assertNoErrors("Invalid command line")
errorReporting.verbosity = c0.verbosity.getOrElse(0)
if (c0.inputFileNames.isEmpty) {
errorReporting.verbosity = c1.verbosity.getOrElse(0)
if (c1.inputFileNames.isEmpty && !c1.languageServer) {
errorReporting.fatalQuit("No input files")
}

Expand All @@ -51,14 +56,14 @@ object Main {
errorReporting.trace("This program comes with ABSOLUTELY NO WARRANTY.")
errorReporting.trace("This is free software, and you are welcome to redistribute it under certain conditions")
errorReporting.trace("You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/")
val c = fixMissingIncludePath(c0).filloutFlags()
val c = fixMissingIncludePath(c1).filloutFlags()
if (c.includePath.isEmpty) {
errorReporting.warn("Failed to detect the default include directory, consider using the -I option")
}

val textCodecRepository = new TextCodecRepository("." :: c.includePath)
val platform = Platform.lookupPlatformFile("." :: c.includePath, c.platform.getOrElse {
errorReporting.info("No platform selected, defaulting to `c64`")
if (!c1.languageServer) errorReporting.info("No platform selected, defaulting to `c64`")
"c64"
}, textCodecRepository)
val options = CompilationOptions(platform, c.flags, c.outputFileName, c.zpRegisterSize.getOrElse(platform.zpRegisterSize), c.features, textCodecRepository, JobContext(errorReporting, new LabelGenerator))
Expand All @@ -67,6 +72,25 @@ object Main {
case (f, b) => errorReporting.debug(f" $f%-30s : $b%s")
}

if (c1.languageServer) {
// We cannot log anything to stdout when starting the language server (otherwise it's a protocol violation)
errorReporting.setOutput(true)
val server = new MfLanguageServer(c, options)

val exec = Executors.newCachedThreadPool()

val launcher = new Launcher.Builder[MfLanguageClient]()
.setExecutorService(exec)
.setInput(System.in)
.setOutput(System.out)
.setRemoteInterface(classOf[MfLanguageClient])
.setLocalService(server)
.create()
val clientProxy = launcher.getRemoteProxy
server.client = Some(clientProxy)
launcher.startListening().get()
}

val output = c.outputFileName match {
case Some(ofn) => ofn
case None => c.inputFileNames match {
Expand Down Expand Up @@ -252,7 +276,7 @@ object Main {
val unoptimized = new MosSourceLoadingQueue(
initialFilenames = c.inputFileNames,
includePath = c.includePath,
options = options).run()
options = options).run().compilationOrderProgram

val program = if (optLevel > 0) {
OptimizationPresets.NodeOpt.foldLeft(unoptimized)((p, opt) => p.applyNodeOptimization(opt, options))
Expand Down Expand Up @@ -306,7 +330,7 @@ object Main {
val unoptimized = new ZSourceLoadingQueue(
initialFilenames = c.inputFileNames,
includePath = c.includePath,
options = options).run()
options = options).run().compilationOrderProgram

val program = if (optLevel > 0) {
OptimizationPresets.NodeOpt.foldLeft(unoptimized)((p, opt) => p.applyNodeOptimization(opt, options))
Expand Down Expand Up @@ -346,7 +370,7 @@ object Main {
val unoptimized = new MSourceLoadingQueue(
initialFilenames = c.inputFileNames,
includePath = c.includePath,
options = options).run()
options = options).run().compilationOrderProgram

val program = if (optLevel > 0) {
OptimizationPresets.NodeOpt.foldLeft(unoptimized)((p, opt) => p.applyNodeOptimization(opt, options))
Expand Down Expand Up @@ -376,7 +400,7 @@ object Main {
val unoptimized = new ZSourceLoadingQueue(
initialFilenames = c.inputFileNames,
includePath = c.includePath,
options = options).run()
options = options).run().compilationOrderProgram

val program = if (optLevel > 0) {
OptimizationPresets.NodeOpt.foldLeft(unoptimized)((p, opt) => p.applyNodeOptimization(opt, options))
Expand Down Expand Up @@ -429,6 +453,15 @@ object Main {
c.copy(outputLabels = true, outputLabelsFormatOverride = Some(f))
}.description("Generate also the label file in the given format. Available options: vice, nesasm, sym.")

flag("-lsp").action { c =>
c.copy(languageServer = true)
}.description("Start the Millfork language server. Does not start compilation.")

parameter("-c", "--config").placeholder("<file>").action { (p, c) =>
assertNone(c.outputFileName, "Config file already defined")
c.copy(configFilePath = Some(p))
}.description("The Millfork config file. Suppliments the provided CLI options.")

boolean("-fbreakpoints", "-fno-breakpoints").action((c,v) =>
c.changeFlag(CompilationFlag.EnableBreakpoints, v)
).description("Include breakpoints in the label file. Requires either -g or -G.")
Expand Down
65 changes: 65 additions & 0 deletions src/main/scala/millfork/cli/JsonConfigParser.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package millfork.cli

import net.liftweb.json._
import java.nio.file.Files
import java.nio.file.Paths
import java.nio.charset.StandardCharsets
import scala.collection.mutable
import scala.collection.convert.ImplicitConversionsToScala._
import java.io.InputStreamReader
import millfork.Context
import millfork.error.ConsoleLogger

case class JsonConfig(
include: Option[List[String]],
platform: Option[String],
inputFiles: Option[List[String]]
)

object JsonConfigParser {
implicit val formats = DefaultFormats

def parseConfig(context: Context, logger: ConsoleLogger): Context = {
var newContext = context

var defaultConfig = false
val filePath = context.configFilePath.getOrElse({
defaultConfig = true
".millforkrc.json"
})

val path = Paths.get(filePath)

try {
val jsonString =
Files
.readAllLines(path, StandardCharsets.UTF_8)
.toIndexedSeq
.mkString("")

val result = parse(jsonString).extract[JsonConfig]

if (context.inputFileNames.length < 1 && result.inputFiles.isDefined) {
newContext = newContext.copy(inputFileNames = result.inputFiles.get)
}

if (context.includePath.length < 1 && result.include.isDefined) {
newContext =
newContext.copy(extraIncludePath = result.include.get.toSeq)
}

if (context.platform.isEmpty && result.platform.isDefined) {
newContext = newContext.copy(platform = Some(result.platform.get))
}
} catch {
case default: Throwable => {
if (!defaultConfig) {
// Only throw error if not default config
logger.fatalQuit("Invalid config file")
}
}
}

newContext
}
}
Loading