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

Recompile on Edit [RIP67] #73

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ lazy val riddlIdeaPlugin: Project = Root(
With.build_info,
With.coverage(90),
With.aliases,
With.riddl("0.54.1")
With.riddl("0.55.0-1-c185a0c5-20241121-1150")
AlexWeinstein92 marked this conversation as resolved.
Show resolved Hide resolved
)
.enablePlugins(KotlinPlugin, JavaAppPackaging)
.settings(
Expand Down
2 changes: 1 addition & 1 deletion project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ object Dep {
"com.eclipsesource.minimal-json" % "minimal-json" % "0.9.5" withSources ()
}
val kotlin = "org.jetbrains.kotlin" % "kotlin-stdlib" % "2.0.20"
val riddlCommands = "com.ossuminc" % "riddl-commands_3" % "0.54.1"
val riddlCommands = "com.ossuminc" % "riddl-commands_3" % "0.55.0-1-c185a0c5-20241121-1150"
AlexWeinstein92 marked this conversation as resolved.
Show resolved Hide resolved

val basic: Seq[ModuleID] = Seq(minimalJson, scalactic, scalatest, scalacheck)

Expand Down
2 changes: 1 addition & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
addSbtPlugin("com.ossuminc" % "sbt-ossuminc" % "0.16.2")
addSbtPlugin("com.ossuminc" % "sbt-ossuminc" % "0.17.1")
addSbtPlugin("org.jetbrains" % "sbt-idea-plugin" % "3.26.2")
addSbtPlugin("org.jetbrains.scala" % "sbt-kotlin-plugin" % "3.0.3")

Expand Down
4 changes: 3 additions & 1 deletion resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@
</actions>

<projectListeners>
<listener class="com.ossuminc.riddl.plugins.idea.files.RiddlFileListenerHighlighter"
<listener class="com.ossuminc.riddl.plugins.idea.files.RiddlFileEditorListener"
topic="com.intellij.openapi.fileEditor.FileEditorManagerListener"/>
<listener class="com.ossuminc.riddl.plugins.idea.files.RiddlDocumentListener"
topic="com.intellij.openapi.editor.event.DocumentListener"/>
</projectListeners>

</idea-plugin>
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,64 @@ import com.ossuminc.riddl.plugins.idea.files.utils.{
getWholeWordsSubstrings,
highlightKeywordsOnChange
}
import com.ossuminc.riddl.plugins.idea.utils.highlightForErrorMessage
import com.ossuminc.riddl.plugins.idea.utils.ManagerBasedGetterUtils.*
import com.ossuminc.riddl.plugins.idea.utils.ParsingUtils.*
import com.ossuminc.riddl.plugins.idea.files.RiddlTokenizer.*

import java.nio.file.Path

class RiddlDocumentListener extends DocumentListener {
override def documentChanged(event: DocumentEvent): Unit = {
val doc = event.getDocument

val editors = EditorFactory.getInstance().getEditors(doc)
if editors.nonEmpty then
val newText = doc.getText(
new TextRange(event.getOffset, event.getOffset + event.getNewLength)
)
val wholeWords = getWholeWordsSubstrings(
doc.getText,
newText,
event.getOffset
)
highlightKeywordsOnChange(
wholeWords
.zip(
RiddlTokenizer
.tokenize(wholeWords.map(_._1).mkString(" "))
.filter(!_._1.isBlank)
editors.find(_.getDocument == doc) match
case Some(editor) if doc.getText.nonEmpty =>
val newText = doc.getText(
new TextRange(event.getOffset, event.getOffset + event.getNewLength)
)
val wholeWords: Seq[(String, Int)] = getWholeWordsSubstrings(
doc.getText,
newText,
event.getOffset
)
.map((wwTup, tokTup) => (wwTup._1, wwTup._2, tokTup._3)),
editors.head
)
highlightKeywordsOnChange(
wholeWords
.zip(
RiddlTokenizer
.tokenize(wholeWords.map(_._1).mkString(" "))
.filter(!_._1.isBlank)
)
AlexWeinstein92 marked this conversation as resolved.
Show resolved Hide resolved
.map((wwTup, tokTup) => (wwTup._1, wwTup._2, tokTup._3)),
editor
)

if editor.getVirtualFile != null then
val editorFilePath = editor.getVirtualFile.getPath
getRiddlIdeaStates.allStates.values.toSeq
.filter { state =>
state.getTopLevelPath.exists(path =>
Path.of(path).compareTo(Path.of(editorFilePath)) <= 0
)
}
.foreach { state =>
state.getMessages
.filter(msg => editorFilePath.endsWith(msg.loc.source.origin))
.foreach { msg =>
runCommandForEditor(
state.getWindowNum,
editor.getVirtualFile.getPath
)
highlightForErrorMessage(
state,
Right(msg.message),
Right((msg.loc, msg.kind.severity))
)
}
}

case _ => ()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.ossuminc.riddl.plugins.idea.files

import com.intellij.openapi.fileEditor.{
FileEditorManager,
FileEditorManagerEvent,
FileEditorManagerListener,
}
import com.intellij.openapi.vfs.VirtualFile
import com.ossuminc.riddl.plugins.idea.utils.ManagerBasedGetterUtils.getProject
import com.ossuminc.riddl.plugins.idea.files.utils.highlightKeywordsAndErrorsForFile

class RiddlFileEditorListener extends FileEditorManagerListener {
override def fileOpened(
source: FileEditorManager,
file: VirtualFile
): Unit = highlightKeywordsAndErrorsForFile(source, file)

override def selectionChanged(event: FileEditorManagerEvent): Unit =
if event.getNewFile != null then
highlightKeywordsAndErrorsForFile(
FileEditorManager
.getInstance(getProject),
event.getNewFile
)
}

This file was deleted.

66 changes: 59 additions & 7 deletions src/main/scala/com/ossuminc/riddl/plugins/idea/files/utils.scala
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
package com.ossuminc.riddl.plugins.idea.files

import com.intellij.openapi.fileEditor.{
TextEditor,
FileDocumentManager,
FileEditorManager
}
import com.intellij.openapi.editor.{DefaultLanguageHighlighterColors, Editor}
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.editor.markup.{
HighlighterLayer,
HighlighterTargetArea
}
import com.intellij.openapi.editor.colors.TextAttributesKey
import com.ossuminc.riddl.plugins.idea.files.RiddlTokenizer.*
import com.ossuminc.riddl.plugins.idea.utils.highlightErrorForFile
import com.ossuminc.riddl.plugins.idea.settings.RiddlIdeaSettings
import com.ossuminc.riddl.plugins.idea.utils.ManagerBasedGetterUtils.getRiddlIdeaStates

object utils {
def splitByBlanks(str: String): Array[String] =
str.split("((?<=\\s)(?=\\S)|(?<=\\S)(?=\\s))")

def getWholeWordsSubstrings(
doc: String,
eventText: String,
newText: String,
start: Int
): Seq[(String, Int)] = {
def getSubStringsWithIndices(index: Int, text: String): (String, Int) = {
val indexedStart = index + start
def getSubStringsWithIndices(offset: Int, text: String): (String, Int) = {
val indexedStart = offset + start
val adjustedStart =
if !doc.charAt(indexedStart).isWhitespace && doc
.lastIndexWhere(_.isWhitespace, indexedStart) > -1
Expand Down Expand Up @@ -51,10 +60,26 @@ object utils {
)
}

if !eventText.isBlank then
val splitText: Seq[String] = splitByBlanks(eventText).toSeq
if newText.isBlank && newText != "\b" then
Seq(
getSubStringsWithIndices(
if start > 0 && doc.charAt(start - 1).isWhitespace then -2
else if start > 0 then -1
else 0,
""
),
getSubStringsWithIndices(
if doc.charAt(start).isWhitespace || start == 0 then 0 else -1,
""
)
)
else if !newText.isBlank then
val splitText: Seq[String] = splitByBlanks(newText).toSeq
val indices: Seq[Int] = splitText.scanLeft(0)((acc, part) => {
acc + part.length
acc + ("""\b*""".r.findFirstIn(part) match
case Some(backspaces) => part.length - backspaces.length
case None => part.length
)
})
indices
.zip(splitText)
Expand All @@ -63,7 +88,7 @@ object utils {
else if !doc.isBlank then {
Seq(
getSubStringsWithIndices(
if doc.charAt(start).isWhitespace then 0 else -1,
if doc.charAt(start).isWhitespace || start == 0 then 0 else -1,
""
)
)
Expand Down Expand Up @@ -178,4 +203,31 @@ object utils {
)
editor.getContentComponent.repaint()
}

def highlightKeywordsAndErrorsForFile(
source: FileEditorManager,
file: VirtualFile
): Unit = source
.getAllEditors(file)
.foreach { te =>
val doc = FileDocumentManager.getInstance().getDocument(file)
if doc != null then {
te match {
case textEditor: TextEditor =>
highlightKeywords(doc.getText, textEditor.getEditor)
getRiddlIdeaStates.allStates
.foldRight(Seq[RiddlIdeaSettings.State]()) { (tup, acc) =>
tup._2.clearErrorHighlighters()
if tup._2.getMessages.exists(
_.loc.source.root.path == file.getPath
)
then acc :+ tup._2
else acc
}
.foreach { state =>
highlightErrorForFile(state, file.getName)
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.ossuminc.riddl.plugins.idea.settings

import com.intellij.openapi.editor.markup.{MarkupModel, RangeHighlighter}
import com.ossuminc.riddl.language.Messages.Message
import java.nio.file.Path
import com.intellij.openapi.components.{
PersistentStateComponent,
Storage,
Expand Down Expand Up @@ -57,6 +60,7 @@ object RiddlIdeaSettings {

class State(windowNum: Int) {
private var riddlConfPath: Option[String] = None
private var riddlTopLevelPath: Option[String] = None
private var riddlRunOutput: Seq[String] = Seq()
private var autoCompileOnSave: Boolean = true
private var command: String = commands.head
Expand All @@ -70,11 +74,19 @@ object RiddlIdeaSettings {
riddlConfPath.flatMap(readFromOptionsFromConf).toSeq
else Seq()

private var parsedPaths: Seq[Path] = Seq()
private var messages: Seq[Message] = Seq()
private var errorHighlighters: Seq[RangeHighlighter] = Seq()
private var markupModelOpt: Option[MarkupModel] = None

def getWindowNum: Int = windowNum

def setConfPath(newPath: Option[String]): Unit = riddlConfPath = newPath
def getConfPath: Option[String] = riddlConfPath

def setTopLevelPath(newPath: String): Unit = riddlTopLevelPath = Some(newPath)
def getTopLevelPath: Option[String] = riddlTopLevelPath

def prependRunOutput(newOutput: String): Unit = riddlRunOutput =
newOutput +: riddlRunOutput
def appendRunOutput(newOutput: String): Unit = riddlRunOutput :+= newOutput
Expand All @@ -89,10 +101,27 @@ object RiddlIdeaSettings {
def getCommand: String = command

def getCommonOptions: CommonOptions = commonOptions
def setCommonOptions(newCOs: CommonOptions): Unit = {
def setCommonOptions(newCOs: CommonOptions): Unit =
commonOptions = newCOs

def getParsedPaths: Seq[Path] = parsedPaths
def setParsedPaths(newPaths: Seq[Path]): Unit =
parsedPaths = newPaths

def getMessages: Seq[Message] = messages
def setMessages(newMsgs: Seq[Message]): Unit =
messages = newMsgs

def appendErrorHighlighter(rangeHighlighter: RangeHighlighter): Seq[RangeHighlighter] =
errorHighlighters :+ rangeHighlighter
def clearErrorHighlighters(): Unit = {
markupModelOpt.foreach(mm =>
errorHighlighters.foreach(mm.removeHighlighter))
errorHighlighters = Seq()
}

def setMarkupModel(newModel: MarkupModel): Unit = markupModelOpt = Some(newModel)

def setFromOption(newFromOption: String): Unit = fromOption =
Some(newFromOption)
def getFromOption: Option[String] = fromOption
Expand Down
Loading
Loading