├── settings.gradle.kts ├── common ├── testRes │ └── script │ │ ├── die_home_guy_so_disgusting.kts │ │ └── use_compiler_internals.kts ├── res │ ├── icon │ │ ├── Icon.png │ │ ├── moveUp.png │ │ ├── Icon-Full.png │ │ ├── moveDown.png │ │ ├── moveUp@2x.png │ │ ├── moveDown@2x.png │ │ ├── actions │ │ │ ├── Gear.png │ │ │ ├── redo.png │ │ │ ├── undo.png │ │ │ ├── Gear@2x.png │ │ │ ├── execute.png │ │ │ ├── redo@2x.png │ │ │ ├── refresh.png │ │ │ ├── undo@2x.png │ │ │ ├── copy_dark.png │ │ │ ├── dump_dark.png │ │ │ ├── execute@2x.png │ │ │ ├── exit_dark.png │ │ │ ├── menu-open.png │ │ │ ├── menu-paste.png │ │ │ ├── refresh@2x.png │ │ │ ├── search_dark.png │ │ │ ├── compile_dark.png │ │ │ ├── copy@2x_dark.png │ │ │ ├── dump@2x_dark.png │ │ │ ├── exit@2x_dark.png │ │ │ ├── menu-cut_dark.png │ │ │ ├── menu-open@2x.png │ │ │ ├── menu-paste@2x.png │ │ │ ├── menu-saveall.png │ │ │ ├── replace_dark.png │ │ │ ├── synchronizeFS.png │ │ │ ├── compile@2x_dark.png │ │ │ ├── menu-cut@2x_dark.png │ │ │ ├── menu-saveall@2x.png │ │ │ ├── replace@2x_dark.png │ │ │ ├── search@2x_dark.png │ │ │ ├── selectall_dark.png │ │ │ ├── synchronizeFS@2x.png │ │ │ └── selectall@2x_dark.png │ │ ├── fileTypes │ │ │ ├── java.png │ │ │ ├── gradle.png │ │ │ ├── any_type.png │ │ │ ├── archive.png │ │ │ ├── gradle@2x.png │ │ │ ├── java@2x.png │ │ │ ├── javaClass.png │ │ │ ├── any_type@2x.png │ │ │ ├── archive@2x.png │ │ │ └── javaClass@2x.png │ │ ├── kotlin │ │ │ ├── kotlin_js.png │ │ │ ├── kotlin_file.png │ │ │ ├── kotlin_js@2x.png │ │ │ ├── kotlin@288x288.png │ │ │ ├── kotlin_file@2x.png │ │ │ ├── kotlin_activity.png │ │ │ ├── kotlin_activity@2x.png │ │ │ ├── kotlin_launch_configuration.png │ │ │ ├── kotlin_launch_configuration@2x.png │ │ │ ├── kotlin_multiplatform_project_dark.png │ │ │ └── kotlin_multiplatform_project@2x_dark.png │ │ └── alternatives │ │ │ ├── clion.png │ │ │ ├── clion@32.png │ │ │ ├── emacs25.png │ │ │ ├── emacs25@2x.png │ │ │ ├── eclipse_dark.png │ │ │ ├── eclipse@2x_dark.png │ │ │ ├── icon_small_dark.png │ │ │ └── icon_small@2x_dark.png │ ├── template │ │ ├── file.kt │ │ ├── js.kt │ │ ├── mp-impl.kt │ │ ├── mp-common.kt │ │ ├── script.kts │ │ ├── activity.kt │ │ └── build.gradle.kts │ └── META-INF │ │ └── services │ │ └── javax.script.ScriptEngineFactory ├── src │ └── org │ │ ├── ice1000 │ │ └── devkt │ │ │ ├── openapi │ │ │ ├── LengthOwner.java │ │ │ ├── ui │ │ │ │ ├── window.kt │ │ │ │ └── ui.kt │ │ │ ├── util │ │ │ │ ├── completions.kt │ │ │ │ └── utils.kt │ │ │ ├── languages.kt │ │ │ ├── psi-utils.kt │ │ │ └── highlight.kt │ │ │ ├── ui │ │ │ ├── options.kt │ │ │ ├── dialogs.kt │ │ │ ├── undo.kt │ │ │ └── DevKtIcons.java │ │ │ ├── defaults.kt │ │ │ ├── package-info.java │ │ │ ├── config │ │ │ └── shortcut.kt │ │ │ ├── lang │ │ │ ├── annotators.kt │ │ │ ├── highlighters.kt │ │ │ └── languages.kt │ │ │ ├── api-wrapper.kt │ │ │ └── analyze.kt │ │ └── jetbrains │ │ └── kotlin │ │ └── com │ │ └── intellij │ │ ├── lexer │ │ ├── DelegateLexer.kt │ │ ├── LexerBase.kt │ │ ├── LookAheadLexer.kt │ │ ├── LayeredLexer.java │ │ └── StringLiteralLexer.kt │ │ └── util │ │ └── containers │ │ ├── ImmutableUserMap.kt │ │ └── IdeaQueue.kt ├── test │ └── org │ │ └── ice1000 │ │ └── devkt │ │ ├── test-properties.kt │ │ └── kts │ │ ├── kts-engine-test.kt │ │ ├── utils.kt │ │ └── kts-test.kt ├── lib │ └── intellij.properties └── build.gradle.kts ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── swing ├── lib │ └── AppleJavaExtensions-1.6.jar ├── src │ ├── lie │ │ ├── package-info.kt │ │ └── mac.kt │ ├── main.kt │ ├── ui │ │ └── swing │ │ │ ├── window.kt │ │ │ ├── forms │ │ │ ├── PsiViewer.form │ │ │ ├── GoToLine.form │ │ │ ├── UI.form │ │ │ └── Find.form │ │ │ ├── dialogs │ │ │ ├── go-to-line.kt │ │ │ ├── psi-viewer.kt │ │ │ └── find-replace.kt │ │ │ ├── utils.kt │ │ │ ├── menu.kt │ │ │ └── swing-impl.kt │ ├── config │ │ └── attributes.kt │ └── defaults.kt ├── test │ └── org │ │ └── ice1000 │ │ └── devkt │ │ ├── ui │ │ ├── swing-tests.kt │ │ ├── MainEditorTest.kt │ │ ├── ConfigurationTest.kt │ │ └── PsiViewerTest.kt │ │ └── benchmark.kt ├── download-font.sh └── build.gradle.kts ├── jfx ├── src │ └── org │ │ └── ice1000 │ │ └── devkt │ │ └── ui │ │ └── jfx │ │ ├── jfx-impl.kt │ │ ├── window.kt │ │ └── non-editor-ui.kt └── build.gradle.kts ├── imgui ├── src │ └── main.kt └── build.gradle.kts ├── .gitignore ├── appveyor.yml ├── .travis.yml ├── .circleci └── config.yml ├── CONTRIBUTING.md ├── gradlew.bat ├── README.md ├── PROGRESS.md └── gradlew /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | include("common", "swing", "jfx", "imgui") -------------------------------------------------------------------------------- /common/testRes/script/die_home_guy_so_disgusting.kts: -------------------------------------------------------------------------------- 1 | System.out.println("我永远喜欢灵乌路空") 2 | -------------------------------------------------------------------------------- /common/res/icon/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/Icon.png -------------------------------------------------------------------------------- /common/res/icon/moveUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/moveUp.png -------------------------------------------------------------------------------- /common/res/icon/Icon-Full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/Icon-Full.png -------------------------------------------------------------------------------- /common/res/icon/moveDown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/moveDown.png -------------------------------------------------------------------------------- /common/res/icon/moveUp@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/moveUp@2x.png -------------------------------------------------------------------------------- /common/res/icon/moveDown@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/moveDown@2x.png -------------------------------------------------------------------------------- /common/res/icon/actions/Gear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/Gear.png -------------------------------------------------------------------------------- /common/res/icon/actions/redo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/redo.png -------------------------------------------------------------------------------- /common/res/icon/actions/undo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/undo.png -------------------------------------------------------------------------------- /common/res/icon/fileTypes/java.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/fileTypes/java.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /common/res/icon/actions/Gear@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/Gear@2x.png -------------------------------------------------------------------------------- /common/res/icon/actions/execute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/execute.png -------------------------------------------------------------------------------- /common/res/icon/actions/redo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/redo@2x.png -------------------------------------------------------------------------------- /common/res/icon/actions/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/refresh.png -------------------------------------------------------------------------------- /common/res/icon/actions/undo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/undo@2x.png -------------------------------------------------------------------------------- /common/res/icon/fileTypes/gradle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/fileTypes/gradle.png -------------------------------------------------------------------------------- /common/res/icon/kotlin/kotlin_js.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/kotlin/kotlin_js.png -------------------------------------------------------------------------------- /common/res/template/file.kt: -------------------------------------------------------------------------------- 1 | package devkt 2 | 3 | fun main(vararg args: String) { 4 | println("Hello World!") 5 | } 6 | -------------------------------------------------------------------------------- /common/res/icon/actions/copy_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/copy_dark.png -------------------------------------------------------------------------------- /common/res/icon/actions/dump_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/dump_dark.png -------------------------------------------------------------------------------- /common/res/icon/actions/execute@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/execute@2x.png -------------------------------------------------------------------------------- /common/res/icon/actions/exit_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/exit_dark.png -------------------------------------------------------------------------------- /common/res/icon/actions/menu-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/menu-open.png -------------------------------------------------------------------------------- /common/res/icon/actions/menu-paste.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/menu-paste.png -------------------------------------------------------------------------------- /common/res/icon/actions/refresh@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/refresh@2x.png -------------------------------------------------------------------------------- /common/res/icon/actions/search_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/search_dark.png -------------------------------------------------------------------------------- /common/res/icon/alternatives/clion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/alternatives/clion.png -------------------------------------------------------------------------------- /common/res/icon/fileTypes/any_type.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/fileTypes/any_type.png -------------------------------------------------------------------------------- /common/res/icon/fileTypes/archive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/fileTypes/archive.png -------------------------------------------------------------------------------- /common/res/icon/fileTypes/gradle@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/fileTypes/gradle@2x.png -------------------------------------------------------------------------------- /common/res/icon/fileTypes/java@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/fileTypes/java@2x.png -------------------------------------------------------------------------------- /common/res/icon/fileTypes/javaClass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/fileTypes/javaClass.png -------------------------------------------------------------------------------- /common/res/icon/kotlin/kotlin_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/kotlin/kotlin_file.png -------------------------------------------------------------------------------- /common/res/icon/kotlin/kotlin_js@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/kotlin/kotlin_js@2x.png -------------------------------------------------------------------------------- /swing/lib/AppleJavaExtensions-1.6.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/swing/lib/AppleJavaExtensions-1.6.jar -------------------------------------------------------------------------------- /common/res/icon/actions/compile_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/compile_dark.png -------------------------------------------------------------------------------- /common/res/icon/actions/copy@2x_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/copy@2x_dark.png -------------------------------------------------------------------------------- /common/res/icon/actions/dump@2x_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/dump@2x_dark.png -------------------------------------------------------------------------------- /common/res/icon/actions/exit@2x_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/exit@2x_dark.png -------------------------------------------------------------------------------- /common/res/icon/actions/menu-cut_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/menu-cut_dark.png -------------------------------------------------------------------------------- /common/res/icon/actions/menu-open@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/menu-open@2x.png -------------------------------------------------------------------------------- /common/res/icon/actions/menu-paste@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/menu-paste@2x.png -------------------------------------------------------------------------------- /common/res/icon/actions/menu-saveall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/menu-saveall.png -------------------------------------------------------------------------------- /common/res/icon/actions/replace_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/replace_dark.png -------------------------------------------------------------------------------- /common/res/icon/actions/synchronizeFS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/synchronizeFS.png -------------------------------------------------------------------------------- /common/res/icon/alternatives/clion@32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/alternatives/clion@32.png -------------------------------------------------------------------------------- /common/res/icon/alternatives/emacs25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/alternatives/emacs25.png -------------------------------------------------------------------------------- /common/res/icon/fileTypes/any_type@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/fileTypes/any_type@2x.png -------------------------------------------------------------------------------- /common/res/icon/fileTypes/archive@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/fileTypes/archive@2x.png -------------------------------------------------------------------------------- /common/res/icon/kotlin/kotlin@288x288.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/kotlin/kotlin@288x288.png -------------------------------------------------------------------------------- /common/res/icon/kotlin/kotlin_file@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/kotlin/kotlin_file@2x.png -------------------------------------------------------------------------------- /common/res/icon/actions/compile@2x_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/compile@2x_dark.png -------------------------------------------------------------------------------- /common/res/icon/actions/menu-cut@2x_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/menu-cut@2x_dark.png -------------------------------------------------------------------------------- /common/res/icon/actions/menu-saveall@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/menu-saveall@2x.png -------------------------------------------------------------------------------- /common/res/icon/actions/replace@2x_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/replace@2x_dark.png -------------------------------------------------------------------------------- /common/res/icon/actions/search@2x_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/search@2x_dark.png -------------------------------------------------------------------------------- /common/res/icon/actions/selectall_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/selectall_dark.png -------------------------------------------------------------------------------- /common/res/icon/actions/synchronizeFS@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/synchronizeFS@2x.png -------------------------------------------------------------------------------- /common/res/icon/alternatives/emacs25@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/alternatives/emacs25@2x.png -------------------------------------------------------------------------------- /common/res/icon/fileTypes/javaClass@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/fileTypes/javaClass@2x.png -------------------------------------------------------------------------------- /common/res/icon/kotlin/kotlin_activity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/kotlin/kotlin_activity.png -------------------------------------------------------------------------------- /common/res/icon/actions/selectall@2x_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/actions/selectall@2x_dark.png -------------------------------------------------------------------------------- /common/res/icon/alternatives/eclipse_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/alternatives/eclipse_dark.png -------------------------------------------------------------------------------- /common/res/icon/kotlin/kotlin_activity@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/kotlin/kotlin_activity@2x.png -------------------------------------------------------------------------------- /common/res/META-INF/services/javax.script.ScriptEngineFactory: -------------------------------------------------------------------------------- 1 | org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmDaemonLocalEvalScriptEngineFactory -------------------------------------------------------------------------------- /common/res/icon/alternatives/eclipse@2x_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/alternatives/eclipse@2x_dark.png -------------------------------------------------------------------------------- /common/res/icon/alternatives/icon_small_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/alternatives/icon_small_dark.png -------------------------------------------------------------------------------- /common/res/icon/alternatives/icon_small@2x_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/alternatives/icon_small@2x_dark.png -------------------------------------------------------------------------------- /common/res/icon/kotlin/kotlin_launch_configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/kotlin/kotlin_launch_configuration.png -------------------------------------------------------------------------------- /common/res/icon/kotlin/kotlin_launch_configuration@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/kotlin/kotlin_launch_configuration@2x.png -------------------------------------------------------------------------------- /common/res/template/js.kt: -------------------------------------------------------------------------------- 1 | package devkt 2 | 3 | fun main(vararg args: String) { 4 | console.log("Hello World!") 5 | js("document.writeln('Hello World!')") 6 | } 7 | -------------------------------------------------------------------------------- /common/res/icon/kotlin/kotlin_multiplatform_project_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/kotlin/kotlin_multiplatform_project_dark.png -------------------------------------------------------------------------------- /common/res/icon/kotlin/kotlin_multiplatform_project@2x_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ice1000/dev-kt/HEAD/common/res/icon/kotlin/kotlin_multiplatform_project@2x_dark.png -------------------------------------------------------------------------------- /common/res/template/mp-impl.kt: -------------------------------------------------------------------------------- 1 | package devkt 2 | 3 | actual class Foo actual constructor(val bar: String) { 4 | actual fun frob() { 5 | println("Frobbing the $bar") 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /common/res/template/mp-common.kt: -------------------------------------------------------------------------------- 1 | package devkt 2 | 3 | expect class Foo(bar: String) { 4 | fun frob() 5 | } 6 | 7 | fun main(args: Array) { 8 | Foo("Hello").frob() 9 | } 10 | -------------------------------------------------------------------------------- /jfx/src/org/ice1000/devkt/ui/jfx/jfx-impl.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.ui.jfx 2 | 3 | /** 4 | * @author ice1000 5 | * @since v2.0 6 | */ 7 | class UIImpl { 8 | fun a() { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /common/src/org/ice1000/devkt/openapi/LengthOwner.java: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.openapi; 2 | 3 | /** 4 | * @author ice1000 5 | * @since v1.2 6 | */ 7 | public interface LengthOwner { 8 | int getLength(); 9 | } 10 | -------------------------------------------------------------------------------- /imgui/src/main.kt: -------------------------------------------------------------------------------- 1 | @file:JvmName("Main") 2 | 3 | package org.ice1000.devkt 4 | 5 | import org.ice1000.jimgui.dsl.runPer 6 | 7 | @JvmName("main") 8 | fun devKt(args: Array) { 9 | runPer(15) { 10 | } 11 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /jfx/src/org/ice1000/devkt/ui/jfx/window.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.ui.jfx 2 | 3 | import javafx.application.Application 4 | import javafx.stage.Stage 5 | 6 | class DevKtApplication : Application() { 7 | override fun start(primaryStage: Stage) { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /common/res/template/script.kts: -------------------------------------------------------------------------------- 1 | import org.ice1000.devkt.config.* 2 | import org.ice1000.devkt.ui.swing.* 3 | import org.ice1000.devkt.ui.* 4 | 5 | val ui = DevKtFrame.instance.ui 6 | 7 | ui.dialog("Hello World!", MessageType.Plain) 8 | ui.message("You're running script now.") 9 | -------------------------------------------------------------------------------- /swing/src/lie/package-info.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Cross platform is a lie, always a lie. 3 | * This package contains platform specific stuffs. 4 | * It is considered evil to have smuggled codes, 5 | * so this package is named `lie`. 6 | * 7 | * @author ice1000, zxj5470 8 | */ 9 | package org.ice1000.devkt.lie 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out/ 2 | *.iml 3 | .idea 4 | .gradle 5 | build 6 | pinpoint_piggy 7 | res/font/*.ttf 8 | config.properties 9 | build-cache 10 | .build-cache 11 | gradle.properties 12 | *.ttf 13 | common/plugins 14 | swing/src/org/ice1000/devkt/ui/swing/forms/*.java 15 | swing/src/org/ice1000/devkt/ui/swing/forms/*.kt 16 | plugins -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | JAVA_HOME: C:\Program Files\Java\jdk11 3 | # 4 | 5 | build_script: 6 | - gradlew :common:downloadFiraCode --info --warning-mode=all 7 | - gradlew :swing:fatJar --info --warning-mode=all 8 | # 9 | 10 | artifacts: 11 | - path: 'swing\build\libs\*.jar' 12 | name: snapshot 13 | # 14 | -------------------------------------------------------------------------------- /swing/test/org/ice1000/devkt/ui/swing-tests.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.ui 2 | 3 | import charlie.gensokyo.exitOnClose 4 | import charlie.gensokyo.show 5 | import charlie.gensokyo.size 6 | import javax.swing.JFrame 7 | 8 | fun main(args: Array) { 9 | JFrame().apply { 10 | size(100, 100) 11 | exitOnClose 12 | }.show 13 | } 14 | -------------------------------------------------------------------------------- /common/res/template/activity.kt: -------------------------------------------------------------------------------- 1 | package devkt 2 | 3 | import android.support.v7.app.AppCompatActivity 4 | import android.os.Bundle 5 | 6 | class MainActivity : AppCompatActivity() { 7 | override fun onCreate(savedInstanceState: Bundle?) { 8 | super.onCreate(savedInstanceState) 9 | setContentView(R.layout.activity_main) 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /common/src/org/ice1000/devkt/ui/options.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.ui 2 | 3 | /** 4 | * @author ice1000 5 | * @since v1.3 6 | */ 7 | enum class MessageType { 8 | Error, Information, Plain, Question, Warning 9 | } 10 | 11 | /** 12 | * @author ice1000 13 | * @since v1.3 14 | */ 15 | enum class ChooseFileType { 16 | Open, Save, Create 17 | } 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | language: kotlin 3 | before_cache: 4 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 5 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ 6 | cache: 7 | directories: 8 | - $HOME/.gradle/caches/ 9 | - $HOME/.gradle/wrapper/ 10 | jdk: 11 | - openjdk11 12 | script: 13 | - bash gradlew test --info --warning-mode=all 14 | -------------------------------------------------------------------------------- /swing/download-font.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # This is just a reference for installing sarasa gothic font 4 | # you may download it manually 5 | 6 | # mkdir res 7 | cd res 8 | mkdir font 9 | cd font 10 | wget https://github.com/be5invis/Sarasa-Gothic/releases/download/v0.5.2/sarasa-gothic-ttf-0.5.2.7z 11 | 7z x sarasa-gothic-ttf-0.5.2.7z 12 | rm sarasa-gothic-ttf-0.5.2.7z 13 | cd .. 14 | cd .. -------------------------------------------------------------------------------- /swing/test/org/ice1000/devkt/ui/MainEditorTest.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.ui 2 | 3 | import org.junit.Test 4 | 5 | 6 | /** 7 | * @author zxj5470 8 | * @date 2018/4/7 9 | */ 10 | class MainEditorTest { 11 | @Test 12 | fun testReplace() { 13 | val java = "java -cp xxxxxx devkt.Main" 14 | val javaExe = "/usr/bin/java" 15 | val fileContent = java.replaceFirst("java", javaExe).replaceFirst(" devkt.", " ") 16 | println(fileContent) 17 | } 18 | } -------------------------------------------------------------------------------- /swing/test/org/ice1000/devkt/ui/ConfigurationTest.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.ui 2 | 3 | import charlie.gensokyo.show 4 | import org.ice1000.devkt.ui.swing.dialogs.ConfigurationImpl 5 | import org.ice1000.devkt.ui.swing.DevKtFrame 6 | import org.ice1000.devkt.ui.swing.UIImpl 7 | import org.junit.Assert 8 | 9 | fun main(args: Array) { 10 | val dialog = ConfigurationImpl(UIImpl(DevKtFrame())) 11 | dialog.show 12 | Assert.assertFalse(dialog.isVisible) 13 | System.exit(0) 14 | } 15 | -------------------------------------------------------------------------------- /common/testRes/script/use_compiler_internals.kts: -------------------------------------------------------------------------------- 1 | fun checkInaccessible(name: String) { 2 | try { 3 | Class.forName(name!!.toString()) 4 | throw AssertionError("Class should not be accessible from script via the class loader: $name") 5 | } catch (e: ClassNotFoundException) { 6 | // OK 7 | } 8 | } 9 | 10 | checkInaccessible("org.jetbrains.kotlin.config.KotlinCompilerVersion") 11 | checkInaccessible("org.jetbrains.kotlin.cli.jvm.K2JVMCompiler") 12 | checkInaccessible("org.jetbrains.kotlin.preloading.Preloader") 13 | System.out.print("OK") 14 | -------------------------------------------------------------------------------- /common/src/org/ice1000/devkt/defaults.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("ClassName", "ObjectPropertyName") 2 | @file:JvmName("Main") 3 | @file:JvmMultifileClass 4 | 5 | package org.ice1000.devkt 6 | 7 | object LaunchInfo { 8 | fun load(vararg args: String) = args.forEach { arg -> 9 | when (arg) { 10 | "--ugly" -> ugly = true 11 | "--no-font" -> noFont = true 12 | "--no-bg" -> noBg = true 13 | "--redirect-stdout" -> redirectStdout = true 14 | } 15 | } 16 | 17 | var ugly = false 18 | var noFont = false 19 | var noBg = false 20 | var redirectStdout = false 21 | } 22 | 23 | const val defaultFontName = "DevKt Default" 24 | -------------------------------------------------------------------------------- /swing/test/org/ice1000/devkt/ui/PsiViewerTest.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.ui 2 | 3 | import charlie.gensokyo.show 4 | import org.ice1000.devkt.Analyzer 5 | import org.ice1000.devkt.ui.swing.dialogs.PsiViewerImpl 6 | import org.junit.Assert.assertFalse 7 | import java.io.IOException 8 | import java.nio.file.Files 9 | import java.nio.file.Paths 10 | 11 | @Throws(IOException::class) 12 | fun main(args: Array) { 13 | val dialog = PsiViewerImpl(Analyzer.parseKotlin(String(Files.readAllBytes(Paths.get( 14 | "src", "org", "ice1000", "devkt", "analyze.kt"))))) 15 | dialog.show 16 | assertFalse(dialog.isVisible) 17 | System.exit(0) 18 | } 19 | -------------------------------------------------------------------------------- /common/test/org/ice1000/devkt/test-properties.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt 2 | 3 | import org.ice1000.devkt.config.GlobalSettings 4 | import kotlin.reflect.full.declaredMemberProperties 5 | import kotlin.reflect.jvm.isAccessible 6 | import kotlin.test.Test 7 | import kotlin.test.assertNotNull 8 | 9 | class TestProperties { 10 | @Test 11 | fun init() { 12 | GlobalSettings::class.declaredMemberProperties.forEach { 13 | if ("configFile" == it.name || "properties" == it.name) return@forEach 14 | it.isAccessible = true 15 | assertNotNull( 16 | it.getDelegate(GlobalSettings) ?: it.get(GlobalSettings), 17 | "${it.name} should not be null") 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /common/src/org/ice1000/devkt/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * FOR DEVELOPERS 3 | * 4 | * By default we support languages listed in {@link org.ice1000.devkt.ui.DevKtDocumentHandler#languages}. 5 | * To add your own language support, create an IntelliJ Platform plugin first. 6 | * 7 | * Than you may take a look at The CovScript Plugin, 8 | * as an example, the codes are very similar to IntelliJ Platform plugin. 9 | * 10 | * You may create a project from this template, 11 | * remember to follow the instructions in its README file. 12 | */ 13 | 14 | package org.ice1000.devkt; 15 | -------------------------------------------------------------------------------- /swing/src/main.kt: -------------------------------------------------------------------------------- 1 | @file:JvmName("Main") 2 | @file:JvmMultifileClass 3 | 4 | package org.ice1000.devkt 5 | 6 | import org.ice1000.devkt.config.GlobalSettings 7 | import org.ice1000.devkt.lie.MacSpecific 8 | import org.ice1000.devkt.ui.swing.DevKtFrame 9 | import org.jetbrains.kotlin.com.intellij.openapi.util.SystemInfo 10 | 11 | /** 12 | * @author ice1000 13 | */ 14 | @JvmName("main") 15 | fun devKt(vararg args: String) { 16 | with(LaunchInfo) { 17 | load(*args) 18 | if (redirectStdout) redirectStdout() 19 | GlobalSettings.load() 20 | if (!ugly) { 21 | if (SystemInfo.isMac) MacSpecific.init() 22 | useDarculaLaf() 23 | } 24 | if (!noFont) DevKtFontManager.loadFont() 25 | DevKtFrame() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /swing/test/org/ice1000/devkt/benchmark.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt 2 | 3 | import org.ice1000.devkt.lie.MacSpecific 4 | import org.ice1000.devkt.ui.swing.DevKtFrame 5 | import org.jetbrains.kotlin.com.intellij.openapi.util.SystemInfo 6 | 7 | /** 8 | * @author ice1000 9 | */ 10 | @JvmName("main") 11 | fun benchmark(vararg args: String) { 12 | val init = System.currentTimeMillis() 13 | if (SystemInfo.isMac) MacSpecific 14 | val time = System.currentTimeMillis() 15 | useDarculaLaf() 16 | val time2 = System.currentTimeMillis() 17 | DevKtFontManager.loadFont() 18 | val time3 = System.currentTimeMillis() 19 | DevKtFrame() 20 | println("${time - init}, ${time2 - time}, ${time3 - time2}, ${System.currentTimeMillis() - time3}") 21 | } 22 | -------------------------------------------------------------------------------- /common/src/org/jetbrains/kotlin/com/intellij/lexer/DelegateLexer.kt: -------------------------------------------------------------------------------- 1 | package org.jetbrains.kotlin.com.intellij.lexer 2 | 3 | import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType 4 | 5 | open class DelegateLexer(val delegate: Lexer) : LexerBase() { 6 | override fun start(buffer: CharSequence, startOffset: Int, endOffset: Int, initialState: Int) = 7 | this.delegate.start(buffer, startOffset, endOffset, initialState) 8 | 9 | override fun getState(): Int = this.delegate.state 10 | override fun getTokenType(): IElementType? = this.delegate.tokenType 11 | override fun getTokenStart(): Int = this.delegate.tokenStart 12 | override fun getTokenEnd(): Int = this.delegate.tokenEnd 13 | override fun advance() = this.delegate.advance() 14 | override fun getBufferSequence(): CharSequence = this.delegate.bufferSequence 15 | override fun getBufferEnd(): Int = this.delegate.bufferEnd 16 | } 17 | -------------------------------------------------------------------------------- /common/lib/intellij.properties: -------------------------------------------------------------------------------- 1 | #Sat Apr 07 02:54:55 CST 2018 2 | colorString=\#008000 3 | windowIcon= 4 | psiViewerMaxCodeLength=30 5 | highlightTokenBased=true 6 | colorCharLiteral=\#008000 7 | colorParentheses=\#000000 8 | colorVariable=\#000000 9 | monoFontName=DevKt Default 10 | jarName=DevKtCompiled.jar 11 | colorBraces=\#000000 12 | backgroundAlpha=180 13 | colorNumbers=\#0000FF 14 | fontSize=18.0 15 | colorBlockComments=\#808080 16 | colorComma=\#000000 17 | highlightSemanticBased=true 18 | colorTemplateEntries=\#000080 19 | colorKeywords=\#000080 20 | javaClassName=DevKtCompiled 21 | colorFunction=\#000000 22 | colorAnnotations=\#808000 23 | windowBounds=65,24,1855,1056 24 | appName=Dev Kt 25 | colorBrackets=\#000000 26 | colorDocComments=\#808080 27 | tabSize=6 28 | colorSemicolon=\#000000 29 | colorOperators=\#000000 30 | gothicFontName=DevKt Default 31 | colorColon=\#000000 32 | colorIdentifiers=\#000000 33 | colorUserTypeRef=\#DD6718 34 | colorLineComments=\#808080 35 | colorTypeParam=\#20999D 36 | colorBackground=\#FFFFFF 37 | colorProperty=\#660E7A 38 | useTab=true 39 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Java Gradle CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-java/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/openjdk:11.0.3-jdk-stretch 11 | 12 | # Specify service dependencies here if necessary 13 | # CircleCI maintains a library of pre-built images 14 | # documented at https://circleci.com/docs/2.0/circleci-images/ 15 | # - image: circleci/postgres:9.4 16 | 17 | working_directory: ~/repo 18 | 19 | environment: 20 | # Customize the JVM maximum heap limit 21 | JVM_OPTS: -Xmx3200m 22 | TERM: dumb 23 | 24 | steps: 25 | - checkout 26 | 27 | - run: gradle dependencies --warning-mode=all 28 | 29 | - run: gradle :common:downloadFiraCode --warning-mode=all 30 | 31 | - run: gradle :swing:fatJar --info --warning-mode=all 32 | 33 | - store_artifacts: 34 | path: swing/build/libs 35 | 36 | - run: gradle test --info --warning-mode=all 37 | -------------------------------------------------------------------------------- /common/src/org/jetbrains/kotlin/com/intellij/lexer/LexerBase.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2013 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.jetbrains.kotlin.com.intellij.lexer 17 | 18 | abstract class LexerBase : Lexer() { 19 | override fun getCurrentPosition(): LexerPosition { 20 | val offset = tokenStart 21 | val intState = state 22 | return LexerPositionImpl(offset, intState) 23 | } 24 | 25 | override fun restore(position: LexerPosition) { 26 | start(bufferSequence, position.offset, bufferEnd, position.state) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /common/src/org/ice1000/devkt/openapi/ui/window.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.openapi.ui 2 | 3 | import org.ice1000.devkt.openapi.util.CompletionElement 4 | import org.ice1000.devkt.openapi.util.CompletionPopup 5 | import org.ice1000.devkt.ui.ChooseFileType 6 | import org.ice1000.devkt.ui.MessageType 7 | import java.io.File 8 | 9 | interface DevKtWindow { 10 | var edited: Boolean 11 | fun refreshTitle() 12 | fun uiThread(lambda: () -> Unit) 13 | fun doAsync(lambda: () -> Unit) 14 | fun message(text: String) 15 | fun createCompletionPopup(completionList: Collection): CompletionPopup 16 | fun chooseFile(from: File?, chooseFileType: ChooseFileType): File? 17 | fun chooseDir(from: File?, chooseFileType: ChooseFileType): File? 18 | fun dialogYesNo( 19 | text: String, 20 | messageType: MessageType = MessageType.Information, 21 | title: String = messageType.name): Boolean 22 | 23 | fun dialog( 24 | text: String, 25 | messageType: MessageType = MessageType.Information, 26 | title: String = messageType.name) 27 | 28 | fun loadFile(it: File) 29 | fun sync() 30 | fun exit() 31 | fun restart() 32 | } 33 | -------------------------------------------------------------------------------- /common/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import de.undercouch.gradle.tasks.download.Download 2 | import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet 3 | 4 | plugins { 5 | java 6 | maven 7 | id("de.undercouch.download") 8 | kotlin("jvm") 9 | } 10 | 11 | sourceSets { 12 | main { 13 | resources.srcDir("res") 14 | java.srcDir("src") 15 | withConvention(KotlinSourceSet::class) { kotlin.srcDir("src") } 16 | } 17 | 18 | test { 19 | resources.srcDir("testRes") 20 | java.srcDir("test") 21 | withConvention(KotlinSourceSet::class) { kotlin.srcDir("test") } 22 | } 23 | } 24 | 25 | val downloadFiraCode = task("downloadFiraCode") { 26 | src("https://raw.githubusercontent.com/tonsky/FiraCode/master/distr/ttf/FiraCode-Regular.ttf") 27 | dest(file("res/font")) 28 | overwrite(false) 29 | } 30 | 31 | tasks["processResources"].dependsOn(downloadFiraCode) 32 | 33 | dependencies { 34 | val kotlinStable: String by rootProject.extra 35 | 36 | compileOnly(files(*file("lib").listFiles().orEmpty())) 37 | val plugins = file("plugins").listFiles().orEmpty().filterNot { it.isDirectory } 38 | runtimeOnly(files(*plugins.toTypedArray())) 39 | testImplementation(kotlin("reflect", kotlinStable)) 40 | // configurations.runtimeOnly.extendsFrom(configurations.testCompileOnly) 41 | } 42 | -------------------------------------------------------------------------------- /swing/src/lie/mac.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.lie 2 | 3 | import com.apple.eawt.* 4 | import com.apple.eawt.Application.getApplication 5 | import org.ice1000.devkt.config.GlobalSettings 6 | import org.ice1000.devkt.ui.swing.DevKtFrame 7 | import org.jetbrains.kotlin.com.intellij.openapi.util.SystemInfo 8 | import java.awt.event.KeyEvent 9 | 10 | val ctrlOrMeta = if (SystemInfo.isMac) KeyEvent.META_DOWN_MASK else KeyEvent.CTRL_DOWN_MASK 11 | 12 | object MacSpecific : AboutHandler, PreferencesHandler, QuitHandler { 13 | init { 14 | // if System is Mac, make sure set this property before setLookAndFeel 15 | System.getProperties()["apple.laf.useScreenMenuBar"] = "true" 16 | } 17 | 18 | fun init() { 19 | getApplication().let { app -> 20 | app.setPreferencesHandler(this) 21 | app.setQuitHandler(this) 22 | app.setAboutHandler(this) 23 | app.dockIconImage = GlobalSettings.windowIcon.second 24 | } 25 | } 26 | 27 | private val ui 28 | get() = DevKtFrame.instance.ui 29 | 30 | override fun handlePreferences(event: AppEvent.PreferencesEvent) = ui.settings() 31 | // TODO About 32 | override fun handleAbout(event: AppEvent.AboutEvent) = Unit 33 | 34 | override fun handleQuitRequestWith(event: AppEvent.QuitEvent, quitResponse: QuitResponse) = ui.exit() 35 | } 36 | -------------------------------------------------------------------------------- /swing/src/ui/swing/window.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.ui.swing 2 | 3 | import charlie.gensokyo.doNothingOnClose 4 | import charlie.gensokyo.show 5 | import org.ice1000.devkt.config.GlobalSettings 6 | import java.awt.event.* 7 | import javax.swing.JFrame 8 | 9 | class DevKtFrame : JFrame() { 10 | companion object { 11 | lateinit var instance: DevKtFrame 12 | } 13 | 14 | val ui = UIImpl(this) 15 | 16 | init { 17 | instance = this 18 | iconImage = GlobalSettings.windowIcon.second 19 | add(ui.mainPanel) 20 | addWindowListener(object : WindowAdapter() { 21 | override fun windowDeactivated(e: WindowEvent?) = GlobalSettings.save() 22 | override fun windowLostFocus(e: WindowEvent?) = GlobalSettings.save() 23 | override fun windowClosing(e: WindowEvent?) { 24 | GlobalSettings.save() 25 | ui.exit() 26 | } 27 | }) 28 | addComponentListener(object : ComponentAdapter() { 29 | override fun componentMoved(e: ComponentEvent?) { 30 | GlobalSettings.windowBounds = bounds 31 | } 32 | 33 | override fun componentResized(e: ComponentEvent?) { 34 | super.componentResized(e) 35 | GlobalSettings.windowBounds = bounds 36 | ui.imageCache = null 37 | } 38 | }) 39 | bounds = GlobalSettings.windowBounds 40 | doNothingOnClose 41 | show 42 | with(ui) { 43 | postInit() 44 | refreshTitle() 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /common/test/org/ice1000/devkt/kts/kts-engine-test.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.kts 2 | 3 | import org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmDaemonLocalEvalScriptEngineFactory 4 | import org.jetbrains.kotlin.script.jsr223.KotlinStandardJsr223ScriptTemplate 5 | import javax.script.ScriptEngineManager 6 | import kotlin.test.Test 7 | import kotlin.test.assertEquals 8 | 9 | class EngineTest { 10 | @Test 11 | fun testJsr223() { 12 | val factory = ScriptEngineManager().getEngineByExtension("kts")!! 13 | //language=kotlin 14 | assertEquals(2, factory.eval("{1+1}()")) 15 | //language=kotlin 16 | assertEquals(3, factory.eval("1.let(2::plus)")) 17 | } 18 | 19 | @Test 20 | fun testNew() { 21 | val factory = KotlinJsr223JvmDaemonLocalEvalScriptEngineFactory() 22 | .scriptEngine 23 | //language=kotlin 24 | assertEquals(2, factory.eval("{1+1}()")) 25 | //language=kotlin 26 | assertEquals(3, factory.eval("1.let(2::plus)")) 27 | } 28 | 29 | @Test 30 | fun testNewDaemon() { 31 | val factory = KotlinJsr223JvmDaemonLocalEvalScriptEngineFactory() 32 | .scriptEngine 33 | //language=kotlin 34 | assertEquals(2, factory.eval("{1+1}()")) 35 | //language=kotlin 36 | assertEquals(3, factory.eval("1.let(2::plus)")) 37 | } 38 | 39 | @Test 40 | fun testQualifiedName() { 41 | println(KotlinStandardJsr223ScriptTemplate::class.qualifiedName) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /swing/src/config/attributes.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("HasPlatformType") 2 | 3 | package org.ice1000.devkt.config 4 | 5 | import org.ice1000.devkt.DevKtFontManager 6 | import org.ice1000.devkt.openapi.ColorScheme 7 | import java.awt.Color 8 | import java.awt.GraphicsEnvironment 9 | import java.awt.font.FontRenderContext 10 | import javax.swing.text.* 11 | 12 | fun swingColorScheme( 13 | settings: GlobalSettings, 14 | context: AbstractDocument.AttributeContext = StyleContext.getDefaultStyleContext()) = 15 | ColorScheme(settings, createTabSizeAttributes(settings.tabSize)) { 16 | context.addAttribute(context.emptySet, StyleConstants.Foreground, Color.decode(it)) 17 | } 18 | 19 | private fun createFrc() = FontRenderContext(GraphicsEnvironment 20 | .getLocalGraphicsEnvironment() 21 | .defaultScreenDevice 22 | .defaultConfiguration 23 | .defaultTransform 24 | , false, false) 25 | 26 | /** 27 | * See https://stackoverflow.com/a/33557782/7083401 28 | * Modified a little 29 | */ 30 | fun createTabSizeAttributes(tabSize: Int): SimpleAttributeSet { 31 | val spaceSize = DevKtFontManager.monoFont.getStringBounds(" ", createFrc()).width 32 | val tabWidth = spaceSize * tabSize 33 | val tabs = (1..300).map { TabStop((it * tabWidth).toFloat()) } 34 | val tabSet = TabSet(tabs.toTypedArray()) 35 | val attributes = SimpleAttributeSet() 36 | StyleConstants.setTabSet(attributes, tabSet) 37 | return attributes 38 | } 39 | -------------------------------------------------------------------------------- /common/src/org/jetbrains/kotlin/com/intellij/util/containers/ImmutableUserMap.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2013 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.jetbrains.kotlin.com.intellij.util.containers 17 | 18 | import org.jetbrains.annotations.Contract 19 | import org.jetbrains.kotlin.com.intellij.openapi.util.Key 20 | 21 | /** 22 | * @author peter 23 | */ 24 | abstract class ImmutableUserMap private constructor() { 25 | abstract operator fun get(key: Key): T? 26 | 27 | fun put(key: Key, value: T): ImmutableUserMap = ImmutableUserMapImpl(key, value, this) 28 | 29 | private class ImmutableUserMapImpl(private val myKey: Key, private val myValue: V, private val myNext: ImmutableUserMap) : ImmutableUserMap() { 30 | @Suppress("UNCHECKED_CAST") 31 | override fun get(key: Key) = if (key == myKey) myValue as T else myNext[key] 32 | } 33 | 34 | companion object { 35 | @JvmField 36 | val EMPTY: ImmutableUserMap = object : ImmutableUserMap() { 37 | @Contract(pure = true) 38 | override fun get(key: Key): T? = null 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /jfx/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet 2 | 3 | val commitHash: String by rootProject.extra 4 | val isCI: Boolean by rootProject.extra 5 | val isMac: Boolean by rootProject.extra 6 | val kotlinStable: String by rootProject.extra 7 | 8 | plugins { 9 | java 10 | application 11 | kotlin("jvm") 12 | id ("org.openjfx.javafxplugin") version "0.0.9" 13 | } 14 | 15 | application { 16 | if (isMac) applicationDefaultJvmArgs = listOf("-Xdock:name=Dev-Kt") 17 | mainClassName = "org.ice1000.devkt.Main" 18 | } 19 | 20 | dependencies { 21 | implementation(project(":common")) 22 | testImplementation(project(":common")) 23 | } 24 | 25 | javafx { 26 | modules("javafx.controls", "javafx.fxml") 27 | } 28 | 29 | task("fatJar") { 30 | archiveClassifier.set("all") 31 | description = "Assembles a jar archive containing the main classes and all the dependencies." 32 | group = "build" 33 | from(configurations.runtimeClasspath.get().map { if (it.isDirectory) it as Any else zipTree(it) }) 34 | with(tasks["jar"] as Jar) 35 | } 36 | 37 | tasks.withType { 38 | manifest { 39 | attributes(mapOf("Main-Class" to application.mainClassName, 40 | "SplashScreen-Image" to "icon/kotlin@288x288.png")) 41 | } 42 | } 43 | 44 | sourceSets { 45 | main { 46 | resources.setSrcDirs(listOf("res")) 47 | java.setSrcDirs(listOf("src")) 48 | withConvention(KotlinSourceSet::class) { 49 | kotlin.setSrcDirs(listOf("src")) 50 | } 51 | } 52 | 53 | test { 54 | resources.setSrcDirs(listOf("testRes")) 55 | java.setSrcDirs(listOf("test")) 56 | withConvention(KotlinSourceSet::class) { 57 | kotlin.setSrcDirs(listOf("test")) 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /common/src/org/ice1000/devkt/openapi/util/completions.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.openapi.util 2 | 3 | import org.ice1000.devkt.openapi.ui.IDevKtDocumentHandler 4 | import org.ice1000.devkt.ui.DevKtDocumentHandler 5 | import java.util.function.Consumer 6 | import javax.swing.Icon 7 | 8 | /** 9 | * Builder? No need! 10 | * 11 | * @author ice1000 12 | * @property text String see [com.intellij.codeInsight.lookup.LookupElementBuilder.create] 13 | * @property lookup String see [com.intellij.codeInsight.lookup.LookupElementBuilder.withLookupString] 14 | * @property tail String see [com.intellij.codeInsight.lookup.LookupElementBuilder.withTailText] 15 | * @property type String see [com.intellij.codeInsight.lookup.LookupElementBuilder.withTypeText] 16 | * @property icon Icon see [com.intellij.codeInsight.lookup.LookupElementBuilder.withIcon] 17 | * @since v1.4 18 | */ 19 | open class CompletionElement 20 | @JvmOverloads 21 | constructor( 22 | val text: Any = "", 23 | val lookup: String = text.toString(), 24 | val tail: String = "", 25 | val type: String = "", 26 | val icon: Icon? = null 27 | ) { 28 | override fun toString() = lookup 29 | override fun hashCode() = text.hashCode() 30 | override fun equals(other: Any?): Boolean { 31 | if (this === other) return true 32 | if (other !is CompletionElement) return false 33 | if (text != other.text) return false 34 | return true 35 | } 36 | 37 | open fun afterInsert(documentHandler: DevKtDocumentHandler<*>) = Unit 38 | } 39 | 40 | /** 41 | * @author ice1000 42 | * @since v1.4 43 | */ 44 | interface CompletionPopup { 45 | fun show() 46 | fun hide() 47 | fun updateItems(completionElement: Collection) 48 | } 49 | -------------------------------------------------------------------------------- /imgui/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet 2 | 3 | val commitHash: String by rootProject.extra 4 | val isCI: Boolean by rootProject.extra 5 | val isMac: Boolean by rootProject.extra 6 | val kotlinStable: String by rootProject.extra 7 | 8 | plugins { 9 | java 10 | application 11 | kotlin("jvm") 12 | } 13 | 14 | application { 15 | if (isMac) applicationDefaultJvmArgs = listOf("-Xdock:name=Dev-Kt") 16 | mainClassName = "org.ice1000.devkt.Main" 17 | } 18 | 19 | dependencies { 20 | implementation(project(":common")) 21 | val jimguiVersion = "v0.14.0" 22 | implementation(group = "org.ice1000.jimgui", name = "core", version = jimguiVersion) 23 | implementation(group = "org.ice1000.jimgui", name = "kotlin-dsl", version = jimguiVersion) 24 | testImplementation(project(":common")) 25 | } 26 | 27 | task("fatJar") { 28 | archiveClassifier.set("all") 29 | description = "Assembles a jar archive containing the main classes and all the dependencies." 30 | group = "build" 31 | from(configurations.runtimeClasspath.get().map { if (it.isDirectory) it as Any else zipTree(it) }) 32 | with(tasks["jar"] as Jar) 33 | } 34 | 35 | tasks.withType { 36 | manifest { 37 | attributes(mapOf("Main-Class" to application.mainClassName, 38 | "SplashScreen-Image" to "icon/kotlin@288x288.png")) 39 | } 40 | } 41 | 42 | sourceSets { 43 | main { 44 | resources.setSrcDirs(listOf("res")) 45 | java.setSrcDirs(listOf("src")) 46 | withConvention(KotlinSourceSet::class) { 47 | kotlin.setSrcDirs(listOf("src")) 48 | } 49 | } 50 | 51 | test { 52 | resources.setSrcDirs(listOf("testRes")) 53 | java.setSrcDirs(listOf("test")) 54 | withConvention(KotlinSourceSet::class) { 55 | kotlin.setSrcDirs(listOf("test")) 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /common/src/org/ice1000/devkt/openapi/languages.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.openapi 2 | 3 | import org.ice1000.devkt.lang.DevKtLanguage 4 | import org.jetbrains.kotlin.com.intellij.lang.Language 5 | import org.jetbrains.kotlin.com.intellij.lang.ParserDefinition 6 | import org.jetbrains.kotlin.com.intellij.lexer.Lexer 7 | import org.jetbrains.kotlin.com.intellij.openapi.project.Project 8 | import org.jetbrains.kotlin.com.intellij.psi.* 9 | import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType 10 | import javax.swing.Icon 11 | 12 | 13 | /** 14 | * Provides default implementations to avoid breaking plugins when adding new APIs. 15 | * 16 | * @author ice1000 17 | * @since v1.2 18 | */ 19 | abstract class ExtendedDevKtLanguage( 20 | override val language: Language, 21 | val parserDefinition: ParserDefinition 22 | ) : DevKtLanguage { 23 | /** 24 | * Creates a lexer for syntax highlight 25 | * 26 | * @param project Project current project, mocked from Kotlin compiler 27 | * @return Lexer the Lexer used for syntax highlight 28 | */ 29 | override fun createLexer(project: Project): Lexer = parserDefinition.createLexer(project) 30 | 31 | /** 32 | * @see Annotator.annotate 33 | */ 34 | override fun annotate(element: PsiElement, document: AnnotationHolder, colorScheme: ColorScheme) { 35 | if (element is PsiErrorElement) document.highlight(element, colorScheme.error) 36 | } 37 | 38 | override val icon: Icon 39 | get() = language.associatedFileType?.icon ?: super.icon 40 | 41 | /** 42 | * @see SyntaxHighlighter.attributesOf 43 | */ 44 | override fun attributesOf(type: IElementType, colorScheme: ColorScheme) = when (type) { 45 | TokenType.BAD_CHARACTER, TokenType.ERROR_ELEMENT -> colorScheme.error 46 | else -> null 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /common/src/org/ice1000/devkt/openapi/util/utils.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.openapi.util 2 | 3 | import org.ice1000.devkt.Analyzer 4 | import org.ice1000.devkt.config.GlobalSettings 5 | import java.io.* 6 | import javax.swing.JOptionPane 7 | 8 | data class Quad(val first: A, val second: B, val third: C, val fourth: D) 9 | 10 | inline fun ignoreException(lambda: () -> Unit) { 11 | try { 12 | lambda() 13 | } catch (ignored: Exception) { 14 | } 15 | } 16 | 17 | inline fun handleException(lambda: () -> Unit) { 18 | try { 19 | lambda() 20 | } catch (e: Throwable) { 21 | val text = StringBuilder() 22 | e.printStackTrace(PrintStream(object : OutputStream() { 23 | override fun write(byte: Int) { 24 | text.append(byte.toChar()) 25 | } 26 | })) 27 | JOptionPane.showMessageDialog( 28 | null, 29 | text, 30 | "Failed to load plugin", 31 | JOptionPane.ERROR_MESSAGE 32 | ) 33 | } 34 | } 35 | 36 | val selfLocation: String = Analyzer::class.java.protectionDomain.codeSource.location.file 37 | val selfLocationFile: File = File(selfLocation) 38 | 39 | val insteadPaired: Map> = mapOf( 40 | '\t' to lazy { if (GlobalSettings.useTab) "\t" else " ".repeat(GlobalSettings.tabSize) } 41 | ) 42 | 43 | val paired = mapOf( 44 | '"' to '"', 45 | '\'' to '\'', 46 | '“' to '”', 47 | '‘' to '’', 48 | '`' to '`', 49 | '(' to ')', 50 | '(' to ')', 51 | '『' to '』', 52 | '「' to '」', 53 | '〖' to '〗', 54 | '【' to '】', 55 | '[' to ']', 56 | '〔' to '〕', 57 | '[' to ']', 58 | '{' to '}', 59 | '{' to '}', 60 | '<' to '>', 61 | '《' to '》', 62 | '〈' to '〉', 63 | '‹' to '›', 64 | '«' to '»' 65 | ) 66 | 67 | fun cutText(string: String, max: Int) = 68 | if (string.length <= max) string else "${string.take(max)}…" 69 | 70 | fun isHex(c: Char): Boolean = isHex(c.toByte()) 71 | 72 | fun isHex(c: Byte): Boolean { 73 | return c in 48..57 || c in 97..102 || c in 65..70 74 | } 75 | -------------------------------------------------------------------------------- /swing/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet 2 | 3 | val commitHash: String by rootProject.extra 4 | val isCI: Boolean by rootProject.extra 5 | val isMac: Boolean by rootProject.extra 6 | val kotlinStable: String by rootProject.extra 7 | 8 | plugins { 9 | java 10 | application 11 | kotlin("jvm") 12 | } 13 | 14 | application { 15 | if (isMac) applicationDefaultJvmArgs = listOf("-Xdock:name=Dev-Kt") 16 | mainClassName = "org.ice1000.devkt.Main" 17 | } 18 | 19 | repositories { maven("https://jitpack.io") } 20 | 21 | dependencies { 22 | implementation(project(":common")) 23 | implementation(group = "com.github.cqjjjzr", name = "Gensokyo", version = "1.2.2") 24 | implementation(group = "net.iharder.dnd", name = "filedrop", version = "2018.1") 25 | compileOnly(files("lib/AppleJavaExtensions-1.6.jar")) 26 | // configurations.runtimeClasspath.extendsFrom(configurations.testCompileOnly) 27 | testImplementation(project(":common")) 28 | } 29 | 30 | task("fatJar") { 31 | archiveClassifier.set("all") 32 | description = "Assembles a jar archive containing the main classes and all the dependencies." 33 | group = "build" 34 | from(configurations.runtimeClasspath.get() 35 | .filter { it.parentFile.name != "plugins" } 36 | .map { if (it.isDirectory) it as Any else zipTree(it) }) 37 | with(tasks["jar"] as Jar) 38 | } 39 | 40 | tasks.withType { 41 | manifest { 42 | attributes(mapOf("Main-Class" to application.mainClassName, 43 | "SplashScreen-Image" to "icon/Icon.png")) 44 | } 45 | } 46 | 47 | sourceSets { 48 | main { 49 | resources.setSrcDirs(listOf("res")) 50 | java.setSrcDirs(listOf("src")) 51 | withConvention(KotlinSourceSet::class) { 52 | kotlin.setSrcDirs(listOf("src")) 53 | } 54 | } 55 | 56 | test { 57 | resources.setSrcDirs(emptyList()) 58 | java.setSrcDirs(emptyList()) 59 | withConvention(KotlinSourceSet::class) { 60 | kotlin.setSrcDirs(listOf("test")) 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to build 2 | 3 | Clone this repo: 4 | 5 | ```shell 6 | $ git clone https://github.com/ice1000/dev-kt.git 7 | ``` 8 | 9 | Then: 10 | 11 | + (Optional) Download and decompress [Sarasa Gothic](https://github.com/be5invis/Sarasa-Gothic/releases) font to `res/font` 12 | + As reference you can see [this shell script](swing/download-font.sh) 13 | + (Optional) Run `gradlew :swing:downloadFiraCode` 14 | + If you don't do this, the editor font will be extremely ugly 15 | + Use `gradlew :swing:fatJar` to build this application 16 | + Run this application with `java -jar swing/build/libs/devkt-[some unimportant text]-all.jar` 17 | 18 | BTW if you don't need the "run" function of DevKt, 19 | you can simply run this application by `gradlew :swing:run`. 20 | 21 | # Contributing guidelines 22 | 23 | ## You must 24 | 25 | 26 | 0. Use as much `@NotNull` and `@Nullable` as you can in Java codes except local variables 27 | 28 | ## You must not 29 | 30 | 0. Break the code style -- use tab indents with spaces aligns 31 | 0. Open pull requests just to fix code style, or use some syntax sugar (DevKt is not SharpLang!) 32 | 0. Add any kind of generated file into the git repo (including the parser!) 33 | 0. Violate the open source license 34 | 35 | ## You should 36 | 37 | 0. Use Kotlin except UI, but if you only know Java, never mind, we can help you convert 38 | 0. Name your files like `xxx-xxx.kt` 39 | 0. Put all highly related classes into a single file 40 | 0. Use English, but we also read Chinese so if you only know Chinese just use it 41 | 0. Write commit message starting with `[ issue id or refactor type ]` 42 | 43 | ## You'd better 44 | 45 | 0. Read http://www.jetbrains.org/display/IJOS/IntelliJ+Coding+Guidelines 46 | 47 | ## You don't have to 48 | 49 | 0. Write comments, except you're using magics. Tell us if you do so 50 | 0. Write tests, because we'll review your codes carefully 51 | 52 | 53 | -------------------------------------------------------------------------------- /jfx/src/org/ice1000/devkt/ui/jfx/non-editor-ui.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.ui.jfx 2 | 3 | import javafx.application.Application 4 | import javafx.scene.control.Alert 5 | import javafx.scene.control.ButtonType 6 | import javafx.scene.control.Dialog 7 | import org.ice1000.devkt.ui.MessageType 8 | import org.ice1000.devkt.ui.UIBase 9 | import org.jetbrains.kotlin.com.intellij.openapi.util.SystemInfo 10 | import java.io.File 11 | 12 | /** 13 | * @param T Unknown ATM 14 | */ 15 | abstract class AbstractJfxUI(private val application: Application) : UIBase() { 16 | private fun initAlert(it: Dialog, title: String) { 17 | it.isResizable = false 18 | it.title = title 19 | it.showAndWait() 20 | } 21 | 22 | override fun dialogYesNo(text: String, messageType: MessageType, title: String): Boolean = 23 | Alert(when (messageType) { 24 | MessageType.Error -> Alert.AlertType.ERROR 25 | MessageType.Information -> Alert.AlertType.INFORMATION 26 | MessageType.Plain -> Alert.AlertType.NONE 27 | MessageType.Question -> Alert.AlertType.CONFIRMATION 28 | MessageType.Warning -> Alert.AlertType.WARNING 29 | }, text, ButtonType.YES, ButtonType.NO).let { 30 | initAlert(it, title) 31 | return@let it.result == ButtonType.YES 32 | } 33 | 34 | override fun dialog(text: String, messageType: MessageType, title: String) { 35 | Alert(when (messageType) { 36 | MessageType.Error -> Alert.AlertType.ERROR 37 | MessageType.Information -> Alert.AlertType.INFORMATION 38 | MessageType.Plain -> Alert.AlertType.NONE 39 | MessageType.Question -> Alert.AlertType.CONFIRMATION 40 | MessageType.Warning -> Alert.AlertType.WARNING 41 | }, text, ButtonType.OK).let { initAlert(it, title) } 42 | } 43 | 44 | override fun doOpen(file: File) = doBrowse(file.toURI().toString()) 45 | override fun doBrowse(url: String) = 46 | if (SystemInfo.isOracleJvm) 47 | application.hostServices.showDocument(url) 48 | else throw UnsupportedOperationException( 49 | "browsing files cannot be done on non-oracle jvm.") 50 | } 51 | -------------------------------------------------------------------------------- /common/src/org/ice1000/devkt/openapi/psi-utils.kt: -------------------------------------------------------------------------------- 1 | @file:JvmName("PsiUtils") 2 | 3 | package org.ice1000.devkt.openapi 4 | 5 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 6 | import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType 7 | 8 | val PsiElement.nodeType: IElementType get() = node.elementType 9 | 10 | inline fun PsiElement.nextSiblingIgnoring(vararg types: IElementType): Psi? { 11 | var next: PsiElement? = nextSibling 12 | while (true) { 13 | val localNext = next ?: return null 14 | next = localNext.nextSibling 15 | return if (types.any { localNext.node.elementType == it }) continue 16 | else localNext as? Psi 17 | } 18 | } 19 | 20 | inline fun PsiElement.prevSiblingIgnoring(vararg types: IElementType): Psi? { 21 | var next: PsiElement? = prevSibling 22 | while (true) { 23 | val localNext = next ?: return null 24 | next = localNext.prevSibling 25 | return if (types.any { localNext.node.elementType == it }) continue 26 | else localNext as? Psi 27 | } 28 | } 29 | 30 | fun PsiElement.prevSiblingIgnoring(clazz: Class, vararg types: IElementType): Psi? { 31 | var next: PsiElement? = prevSibling 32 | while (true) { 33 | val localNext = next ?: return null 34 | next = localNext.prevSibling 35 | @Suppress("UNCHECKED_CAST") 36 | return if (types.any { localNext.node.elementType == it }) continue 37 | else if (clazz.isAssignableFrom(localNext.javaClass)) localNext as Psi else null 38 | } 39 | } 40 | 41 | fun PsiElement.nextSiblingIgnoring(clazz: Class, vararg types: IElementType): Psi? { 42 | var next: PsiElement? = nextSibling 43 | @Suppress("UNCHECKED_CAST") 44 | while (true) { 45 | val localNext = next ?: return null 46 | next = localNext.nextSibling 47 | return if (types.any { localNext.node.elementType == it }) continue 48 | else if (clazz.isAssignableFrom(localNext.javaClass)) localNext as Psi else null 49 | } 50 | } 51 | 52 | fun PsiElement.childrenBefore(type: IElementType): List { 53 | val ret = ArrayList() 54 | var next: PsiElement? = firstChild 55 | while (true) { 56 | next = next?.nextSibling ?: return ret 57 | if (next.node.elementType == type) return ret 58 | ret += next 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /swing/src/ui/swing/forms/PsiViewer.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
51 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /common/src/org/ice1000/devkt/ui/dialogs.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.ui 2 | 3 | import java.util.regex.Pattern 4 | import java.util.regex.PatternSyntaxException 5 | 6 | data class SearchResult(val start: Int, val end: Int) 7 | 8 | data class FindDataBundle( 9 | var findInput: String, 10 | var isMatchCase: Boolean, 11 | var isRegex: Boolean, 12 | var replaceInput: String? = null 13 | ) 14 | 15 | interface IFind { 16 | companion object { 17 | val NO_REGEXP_CHARS = "\\{[(+*^\$.?|".toCharArray() 18 | } 19 | 20 | val searchResult: MutableList 21 | var currentIndex: Int 22 | val document: DevKtDocumentHandler<*> 23 | 24 | @JvmDefault 25 | fun search(bundle: FindDataBundle) { 26 | searchResult.clear() 27 | currentIndex = 0 28 | document.selectionEnd = document.selectionStart 29 | 30 | if (bundle.findInput.isEmpty()) { 31 | update() 32 | return 33 | } 34 | 35 | val input = bundle.findInput 36 | val text = document.text 37 | val regex = if (!bundle.isRegex) NO_REGEXP_CHARS.fold(input) { last, current -> 38 | last.replace(current.toString(), "\\$current") 39 | } else input 40 | 41 | try { 42 | Pattern.compile( 43 | regex, 44 | if (bundle.isMatchCase.not()) Pattern.CASE_INSENSITIVE or Pattern.UNICODE_CASE else 0 45 | ).matcher(text).run { 46 | while (find()) searchResult += SearchResult(start(), end()) 47 | } 48 | select(0) 49 | } catch (e: PatternSyntaxException) { 50 | document.window.dialog("Invalid regex: $regex", MessageType.Error) 51 | } 52 | } 53 | 54 | @JvmDefault 55 | fun select(index: Int) { 56 | searchResult.getOrNull(index)?.let { (start, end) -> 57 | currentIndex = index 58 | document.selectionStart = start 59 | document.selectionEnd = end 60 | 61 | } 62 | 63 | update() 64 | } 65 | 66 | @JvmDefault 67 | fun moveUp() = select(currentIndex - 1) 68 | 69 | @JvmDefault 70 | fun moveDown() = select(currentIndex + 1) 71 | 72 | fun update() 73 | } 74 | 75 | interface IReplace : IFind { 76 | @JvmDefault 77 | fun replaceCurrent(bundle: FindDataBundle) { 78 | searchResult.getOrNull(currentIndex)?.run { 79 | document.resetTextTo(document.text.replaceRange(start until end, bundle.replaceInput ?: return)) 80 | } 81 | } 82 | 83 | @JvmDefault 84 | fun replaceAll(bundle: FindDataBundle) { 85 | val findInput = bundle.findInput 86 | val replaceInput = bundle.replaceInput ?: return 87 | document.resetTextTo(if (bundle.isRegex) { 88 | document.replaceText(Regex(findInput), replaceInput) 89 | } else document.text.replace(findInput, replaceInput)) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DevKt 2 | 3 | [![](https://jitpack.io/v/ice1000/dev-kt.svg)](https://jitpack.io/#ice1000/dev-kt) 4 | 5 | CI|Status 6 | :---:|:---: 7 | Travis CI (test, no artifact)|[![Build Status](https://travis-ci.org/ice1000/dev-kt.svg?branch=master)](https://travis-ci.org/ice1000/dev-kt) 8 | AppVeyor (artifact, no test)|[![Build status](https://ci.appveyor.com/api/projects/status/c0aq16ej7415m302?svg=true)](https://ci.appveyor.com/project/ice1000/dev-kt) 9 | CircleCI (both)|[![CircleCI](https://circleci.com/gh/ice1000/dev-kt.svg?style=svg)](https://circleci.com/gh/ice1000/dev-kt) 10 | 11 | This is a DevCpp-like cross-platform Kotlin (and Java, experimental) IDE features in lightweight. 12 | 13 | You can download a snapshot [here](https://ci.appveyor.com/project/ice1000/dev-kt/build/artifacts), the one ends with "-all.jar" is an executable jar. 14 | 15 | Here are some screenshots: 16 | 17 | ![devkt-2](https://user-images.githubusercontent.com/16398479/39244656-e0537d3a-48c3-11e8-8c52-301e11dd204d.gif) 18 | 19 | 20 | 21 | # Features 22 | 23 | + Fast (at least faster than Emacs/Eclipse/IntelliJ/CLion/VSCode/Atom) 24 | + Lightweight (Just a tiny Java Swing application) 25 | + Kotlin compiler integration (**100% correct parsing**) 26 | + JetBrains IDE icons 27 | + Build as jar/class files, run after build, just one click 28 | + Cross platform (windows/macos/linux), just an executable jar 29 | + One property-based configuration file, hackable 30 | + Experimental Java support 31 | + Plugin system based on `ServiceLoader` 32 | 33 | Just a simple comparison: 34 | 35 | DevKt

Correct| 36 | :---:|:---: 37 | IntelliJ IDEA

Correct,
with inspections| 38 | Emacs

Incorrect| 39 | VSCode

Incorrect| 40 | 41 | # For Linux users 42 | 43 | To use the JavaFX version on Linux, please install oraclejdk instead of openjdk: 44 | 45 | ``` 46 | $ sudo add-apt-repository ppa:webupd8team/java 47 | $ sudo apt-get update 48 | $ sudo apt-get install oracle-java8-installer 49 | ``` 50 | 51 | # Plugin development guide 52 | 53 | See https://devkt-plugins.github.io 54 | 55 | To install a plugin, just add the jar in the classpath, and you don't need to do anything else. 56 | -------------------------------------------------------------------------------- /common/src/org/ice1000/devkt/config/shortcut.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("EnumEntryName") 2 | 3 | package org.ice1000.devkt.config 4 | 5 | import org.ice1000.devkt.config.Key.Companion.ALT_DOWN_MASK 6 | import org.ice1000.devkt.config.Key.Companion.CTRL_DOWN_MASK 7 | import org.ice1000.devkt.config.Key.Companion.SHIFT_DOWN_MASK 8 | import java.awt.event.KeyEvent 9 | 10 | /** 11 | * @author HoshinoTented, ice1000 12 | * @since v1.2 13 | */ 14 | class ShortCut { 15 | companion object Modifiers { 16 | fun valueOf(str: String) = try { 17 | str.split("|").let { (keyCode, modifier) -> 18 | ShortCut(modifier.toInt(), Key.valueOf(keyCode)) 19 | } 20 | } catch (e: Throwable) { 21 | null 22 | } 23 | } 24 | 25 | val isControl: Boolean 26 | val isAlt: Boolean 27 | val isShift: Boolean 28 | val key: Key 29 | val modifier: Int 30 | 31 | constructor(isControl: Boolean, isAlt: Boolean, isShift: Boolean, keyCode: Key) { 32 | this.isControl = isControl 33 | this.isAlt = isAlt 34 | this.isShift = isShift 35 | this.key = keyCode 36 | this.modifier = (if (isControl) CTRL_DOWN_MASK else 0) or 37 | (if (isAlt) ALT_DOWN_MASK else 0) or 38 | (if (isShift) SHIFT_DOWN_MASK else 0) 39 | } 40 | 41 | /** 42 | * @param modifier Int 43 | * @param key Int 44 | * @constructor use this constructor to prevent `ctrl` being transformed into `meta` in Mac 45 | */ 46 | constructor(modifier: Int, key: Key) { 47 | this.modifier = modifier 48 | this.key = key 49 | this.isControl = modifier and CTRL_DOWN_MASK != 0 50 | this.isShift = modifier and SHIFT_DOWN_MASK != 0 51 | this.isAlt = modifier and ALT_DOWN_MASK != 0 52 | } 53 | 54 | fun check(e: KeyEvent) = e.modifiers == modifier 55 | 56 | override fun toString(): String = "$key|$modifier" 57 | } 58 | 59 | /** 60 | * @author ice1000 61 | * @since v1.3 62 | */ 63 | enum class Key { 64 | A, 65 | B, 66 | C, 67 | D, 68 | E, 69 | F, 70 | G, 71 | H, 72 | I, 73 | J, 74 | K, 75 | L, 76 | M, 77 | N, 78 | O, 79 | P, 80 | Q, 81 | R, 82 | S, 83 | T, 84 | U, 85 | V, 86 | W, 87 | X, 88 | Y, 89 | Z, 90 | `0`, 91 | `1`, 92 | `2`, 93 | `3`, 94 | `4`, 95 | `5`, 96 | `6`, 97 | `7`, 98 | `8`, 99 | `9`, 100 | F1, 101 | F2, 102 | F3, 103 | F4, 104 | F5, 105 | F6, 106 | F7, 107 | F8, 108 | F9, 109 | F10, 110 | F11, 111 | F12, 112 | SLASH, 113 | SPACE, 114 | UP, 115 | DOWN, 116 | LEFT, 117 | RIGHT, 118 | ENTER; 119 | 120 | companion object { 121 | const val SHIFT_MASK = 1 122 | const val CTRL_MASK = 2 123 | const val META_MASK = 4 124 | const val ALT_MASK = 8 125 | const val ALT_GRAPH_MASK = 32 126 | const val SHIFT_DOWN_MASK = 64 127 | const val CTRL_DOWN_MASK = 128 128 | const val META_DOWN_MASK = 256 129 | const val ALT_DOWN_MASK = 512 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /common/src/org/ice1000/devkt/ui/undo.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.ui 2 | 3 | import org.jetbrains.kotlin.com.intellij.util.containers.Stack 4 | 5 | class Edit(val offset: Int, val text: CharSequence, val isInsert: Boolean) { 6 | fun invert() = Edit(offset, text, !isInsert) 7 | val length get() = text.length 8 | override fun toString() = "(${if (isInsert) "Insert" else "Delete"} [$text] at $offset)" 9 | } 10 | 11 | fun Stack.popOrNull() = if (empty()) null else pop() 12 | fun Stack.peekOrNull() = if (empty()) null else peek() 13 | 14 | /** 15 | * Replacement of [javax.swing.undo.UndoManager] 16 | * 17 | * @author ice1000 18 | * @since v1.3 19 | * @property undoStack Stack Use null as mark 20 | * @property redoStack Stack Use null as mark 21 | * @property canUndo Boolean if there's undo available 22 | * @property canRedo Boolean if there's undo available 23 | */ 24 | class DevKtUndoManager(initialCapacity: Int) { 25 | constructor() : this(160) 26 | 27 | private val undoStack = Stack(initialCapacity) 28 | private val redoStack = Stack(initialCapacity) 29 | val canUndo get() = undoStack.isNotEmpty() 30 | val canRedo get() = redoStack.isNotEmpty() 31 | 32 | @Synchronized 33 | fun clear() { 34 | undoStack.clear() 35 | redoStack.clear() 36 | // println("Cleared.") 37 | } 38 | 39 | @Synchronized 40 | fun undo(host: DevKtDocumentHandler<*>) { 41 | if (!canUndo) return 42 | while (null == undoStack.peek()) undoStack.pop() 43 | // println("Undo:") 44 | generateSequence { undoStack.popOrNull() }.forEach { 45 | // println(" $it") 46 | redoStack.push(it) 47 | if (it.isInsert) host.deleteDirectly(it.offset, it.length, reparse = false) 48 | else host.insertDirectly(it.offset, it.text.toString(), move = 0, reparse = false) 49 | } 50 | // println(" (${undoStack.size}, ${redoStack.size})") 51 | host.reparse() 52 | doneUndo() 53 | } 54 | 55 | @Synchronized 56 | fun redo(host: DevKtDocumentHandler<*>) { 57 | if (!canRedo) return 58 | while (null == redoStack.peek()) redoStack.pop() 59 | // println("Redo:") 60 | generateSequence { redoStack.popOrNull() }.forEach { 61 | // println(" $it") 62 | undoStack.push(it) 63 | if (it.isInsert) host.insertDirectly(it.offset, it.text.toString(), move = 0, reparse = false) 64 | else host.deleteDirectly(it.offset, it.length, reparse = false) 65 | } 66 | // println(" (${undoStack.size}, ${redoStack.size})") 67 | host.reparse() 68 | done() 69 | } 70 | 71 | fun addEdit(edit: Edit) { 72 | undoStack.add(edit) 73 | redoStack.clear() 74 | // println("Added $actions, (${undoStack.size}, ${redoStack.size})") 75 | } 76 | 77 | fun done() { 78 | if (null != undoStack.peekOrNull()) 79 | undoStack.push(null) 80 | } 81 | 82 | private fun doneUndo() { 83 | if (null != redoStack.peekOrNull()) 84 | redoStack.push(null) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /PROGRESS.md: -------------------------------------------------------------------------------- 1 | 2 | # Progress 3 | 4 | + Properties-based settings (hackable!) 5 | + [X] Deals with missing properties 6 | + [X] Smart auto saving 7 | + [X] Highlight color customization 8 | + [X] Hot reload (Due to some limitations of Swing it's really hard to implement) 9 | + [X] Font customization 10 | + [X] Font size customization 11 | + [ ] Text style customization 12 | + [ ] Keymap customization 13 | + [ ] Internationalization 14 | + [ ] Settings UI 15 | + File operations 16 | + [X] Create new file when no files are opened 17 | + [X] Save and sync 18 | + [X] Show in files 19 | + [X] Record recently opened files 20 | + [X] Create new files from templates (kts, kt, kt2js, android activity, multiplatform codes, etc.) 21 | + [X] Drag file to open 22 | + Editor 23 | + [X] Multi-language support 24 | + [X] Undo and redo (by `javax.swing.undo.UndoManager.UndoManager`) 25 | + [X] Copy/paste/cut/select all 26 | + [X] Line number 27 | + [X] Background image 28 | + [X] Insert/delete paired characters 29 | + Highlighting strategy 30 | + [ ] Highlight in daemon 31 | + [ ] Prioritized 32 | + [ ] Incremental 33 | + [ ] Highlight selected token 34 | + [X] Completions based on tokens 35 | + Kotlin 36 | + [X] Lexer based highlights 37 | + [X] Semantic-based highlights 38 | + Java 39 | + [X] Lexer based highlights 40 | + [X] Semantic-based highlights 41 | + [X] Auto indent 42 | + [ ] Smart indent 43 | + [ ] Indent with spaces 44 | + Plugin system 45 | + [X] Load plugins in classpath 46 | + [X] [Official CovScript plugin](https://github.com/covscript/covscript-devkt) 47 | + [X] [Official Clojure plugin based on Clojure-Kit](https://github.com/devkt-plugins/clojure-devkt) (deprecated) 48 | + [X] [Official Clojure plugin based on la-clojuer](https://github.com/devkt-plugins/la-clojure-devkt) 49 | + [X] [Official Julia plugin](https://github.com/devkt-plugins/julia-devkt) 50 | + [X] [Official JSON plugin](https://github.com/devkt-plugins/json-devkt) 51 | + [X] [Official Lua plugin based on EmmyLua](https://github.com/devkt-plugins/emmylua-devkt) 52 | + [ ] Official Zig plugin 53 | + [ ] Official Python plugin (too hard since PyCharm has used countless black magics) 54 | + [ ] Official Groovy plugin 55 | + [ ] Official Ruby plugin 56 | + [ ] Official HTML plugin 57 | + [ ] Official XML plugin 58 | + [ ] Official CSS plugin 59 | + [ ] Official Lice plugin 60 | + [ ] Official Markdown plugin 61 | + [ ] Official Scala plugin 62 | + Build and run (Kotlin specific) 63 | + Build 64 | + [X] Build as class files 65 | + [X] Build as jar 66 | + [ ] Build as javascript module 67 | + Run 68 | + [X] Run as class files 69 | + [X] Run as jar 70 | + [ ] Run as kotlin script 71 | + Others 72 | + [X] Open alternative editors' download page in browser 73 | + [X] MacOS toolbar support 74 | + [X] PsiViewer 75 | + [X] Memory indicator 76 | + [ ] Built-in documentation 77 | -------------------------------------------------------------------------------- /common/src/org/ice1000/devkt/ui/DevKtIcons.java: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.ui; 2 | 3 | import org.ice1000.devkt.openapi.ui.IconLoader; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | import javax.swing.*; 7 | 8 | /** 9 | * Provides icon related utils 10 | * 11 | * @author ice1000 12 | * @since v0.0.1 13 | */ 14 | public interface DevKtIcons { 15 | // Kotlin related 16 | @NotNull Icon KOTLIN_JS = IconLoader.getIcon("/icon/kotlin/kotlin_js.png"); 17 | @NotNull Icon KOTLIN_MP = IconLoader.getIcon("/icon/kotlin/kotlin_multiplatform_project_dark.png"); 18 | @NotNull Icon KOTLIN_FILE = IconLoader.getIcon("/icon/kotlin/kotlin_file.png"); 19 | @NotNull Icon KOTLIN_ANDROID = IconLoader.getIcon("/icon/kotlin/kotlin_activity.png"); 20 | 21 | // Project related 22 | @NotNull Icon OPEN = IconLoader.getIcon("/icon/actions/menu-open.png"); 23 | @NotNull Icon CUT = IconLoader.getIcon("/icon/actions/menu-cut_dark.png"); 24 | @NotNull Icon COPY = IconLoader.getIcon("/icon/actions/copy_dark.png"); 25 | @NotNull Icon PASTE = IconLoader.getIcon("/icon/actions/menu-paste.png"); 26 | @NotNull Icon COMPILE = IconLoader.getIcon("/icon/actions/compile_dark.png"); 27 | @NotNull Icon CLASS = IconLoader.getIcon("/icon/fileTypes/javaClass.png"); 28 | @NotNull Icon EXECUTE = IconLoader.getIcon("/icon/actions/execute.png"); 29 | @NotNull Icon SETTINGS = IconLoader.getIcon("/icon/actions/Gear.png"); 30 | @NotNull Icon DUMP = IconLoader.getIcon("/icon/actions/dump_dark.png"); 31 | @NotNull Icon EXIT = IconLoader.getIcon("/icon/actions/exit_dark.png"); 32 | @NotNull Icon SAVE = IconLoader.getIcon("/icon/actions/menu-saveall.png"); 33 | @NotNull Icon UNDO = IconLoader.getIcon("/icon/actions/undo.png"); 34 | @NotNull Icon REDO = IconLoader.getIcon("/icon/actions/redo.png"); 35 | @NotNull Icon SYNCHRONIZE = IconLoader.getIcon("/icon/actions/synchronizeFS.png"); 36 | @NotNull Icon REFRESH = IconLoader.getIcon("/icon/actions/refresh.png"); 37 | @NotNull Icon JAR = IconLoader.getIcon("/icon/fileTypes/archive.png"); 38 | @NotNull Icon SELECT_ALL = IconLoader.getIcon("/icon/actions/selectall_dark.png"); 39 | @NotNull Icon GRADLE = IconLoader.getIcon("/icon/fileTypes/gradle.png"); 40 | @NotNull Icon MOVE_UP = IconLoader.getIcon("/icon/moveUp.png"); 41 | @NotNull Icon MOVE_DOWN = IconLoader.getIcon("/icon/moveDown.png"); 42 | @NotNull Icon REPLACE = IconLoader.getIcon("/icon/actions/replace_dark.png"); 43 | @NotNull Icon FIND = IconLoader.getIcon("/icon/actions/search_dark.png"); 44 | 45 | // Languages 46 | @NotNull Icon JAVA = IconLoader.getIcon("/icon/fileTypes/java.png"); 47 | @NotNull Icon ANY = IconLoader.getIcon("/icon/fileTypes/any_type.png"); 48 | 49 | // Providers 50 | @NotNull Icon ECLIPSE = IconLoader.getIcon("/icon/alternatives/eclipse_dark.png"); 51 | @NotNull Icon IDEA = IconLoader.getIcon("/icon/alternatives/icon_small_dark.png"); 52 | @NotNull Icon EMACS = IconLoader.getIcon("/icon/alternatives/emacs25.png"); 53 | @NotNull Icon CLION = IconLoader.getIcon("/icon/alternatives/clion.png"); 54 | } 55 | -------------------------------------------------------------------------------- /swing/src/defaults.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("ClassName", "ObjectPropertyName") 2 | @file:JvmName("Main") 3 | @file:JvmMultifileClass 4 | 5 | package org.ice1000.devkt 6 | 7 | import com.bulenkov.darcula.DarculaLaf 8 | import org.ice1000.devkt.config.GlobalSettings 9 | import org.ice1000.devkt.ui.Edit 10 | import org.ice1000.devkt.ui.swing.DevKtFrame 11 | import java.awt.Font 12 | import java.awt.GraphicsEnvironment 13 | import java.io.OutputStream 14 | import java.io.PrintStream 15 | import javax.swing.UIManager 16 | 17 | fun redirectStdout() = System.setOut(PrintStream(object : OutputStream() { 18 | override fun write(b: Int) { 19 | if (b.toChar() == '\n') DevKtFrame.instance.ui.messageLabel.text = "" 20 | else DevKtFrame.instance.ui.messageLabel.run { text = "$text${b.toChar()}" } 21 | } 22 | })) 23 | 24 | object DevKtFontManager { 25 | var monoFont: Font 26 | get() = UIManager.getFont("TextPane.font") 27 | set(value) { 28 | UIManager.put("TextPane.font", value) 29 | UIManager.put("List.font", value) 30 | } 31 | 32 | var gothicFont: Font 33 | get() = UIManager.getFont("Panel.font") 34 | set(value) { 35 | UIManager.put("Menu.font", value) 36 | UIManager.put("MenuBar.font", value) 37 | UIManager.put("MenuItem.font", value) 38 | UIManager.put("Label.font", value) 39 | UIManager.put("Spinner.font", value) 40 | UIManager.put("MenuItem.acceleratorFont", value) 41 | UIManager.put("FormattedTextField.font", value) 42 | UIManager.put("TextField.font", value) 43 | UIManager.put("FileChooser.font", value) 44 | UIManager.put("Button.font", value) 45 | UIManager.put("Table.font", value) 46 | UIManager.put("Panel.font", value) 47 | UIManager.put("CheckBox.font", value) 48 | UIManager.put("ComboBox.font", value) 49 | UIManager.put("ToolTip.font", value) 50 | } 51 | 52 | val allFonts: Array by lazy { 53 | GraphicsEnvironment.getLocalGraphicsEnvironment().availableFontFamilyNames 54 | } 55 | 56 | fun loadFont() { 57 | val mono = GlobalSettings.monoFontName.trim() 58 | if (mono.isEmpty() or 59 | mono.equals(defaultFontName, true)) { 60 | val monoFontInputStream = javaClass.getResourceAsStream("/font/sarasa-mono-sc-regular.ttf") 61 | ?: LaunchInfo::class.java.getResourceAsStream("/font/FiraCode-Regular.ttf") 62 | if (null != monoFontInputStream) 63 | monoFont = Font 64 | .createFont(Font.TRUETYPE_FONT, monoFontInputStream) 65 | .deriveFont(GlobalSettings.fontSize) 66 | } else { 67 | monoFont = Font(mono, Font.TRUETYPE_FONT, 16).deriveFont(GlobalSettings.fontSize) 68 | } 69 | val gothic = GlobalSettings.gothicFontName.trim() 70 | if (gothic.isEmpty() or 71 | gothic.equals(defaultFontName, true)) { 72 | val gothicFontInputStream = javaClass.getResourceAsStream("/font/sarasa-gothic-sc-regular.ttf") 73 | if (null != gothicFontInputStream) 74 | gothicFont = Font 75 | .createFont(Font.TRUETYPE_FONT, gothicFontInputStream) 76 | .deriveFont(GlobalSettings.fontSize) 77 | } else { 78 | gothicFont = Font(gothic, Font.TRUETYPE_FONT, 16).deriveFont(GlobalSettings.fontSize) 79 | } 80 | } 81 | } 82 | 83 | fun useDarculaLaf() { 84 | UIManager.getFont("Label.font") 85 | UIManager.setLookAndFeel(DarculaLaf()) 86 | } 87 | -------------------------------------------------------------------------------- /common/res/template/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.gradle.api.internal.HasConvention 2 | import org.gradle.internal.deployment.RunApplication 3 | import org.jetbrains.kotlin.com.intellij.openapi.util.SystemInfo 4 | import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet 5 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 6 | import java.io.* 7 | import java.nio.file.* 8 | import java.util.concurrent.* 9 | import java.util.stream.Collectors 10 | 11 | val commitHash by lazy { 12 | val process: Process = Runtime.getRuntime().exec("git rev-valueOf --short HEAD") 13 | process.waitFor() 14 | val output = process.inputStream.use { 15 | it.bufferedReader().use(BufferedReader::readText) 16 | } 17 | process.destroy() 18 | output.trim() 19 | } 20 | 21 | val isCI = !System.getenv("CI").isNullOrBlank() 22 | 23 | val pluginShortVersion = "v1.0-SNAPSHOT" 24 | val packageName = "devkt" 25 | val kotlinVersion: String by extra 26 | val pluginCalculatedVersion = if (isCI) "$pluginShortVersion-$commitHash" else pluginShortVersion 27 | 28 | group = packageName 29 | version = pluginCalculatedVersion 30 | 31 | buildscript { 32 | var kotlinVersion: String by extra 33 | kotlinVersion = "1.2.31" 34 | 35 | repositories { 36 | mavenCentral() 37 | } 38 | 39 | dependencies { 40 | classpath(kotlin("gradle-plugin", kotlinVersion)) 41 | } 42 | } 43 | 44 | plugins { 45 | java 46 | application 47 | kotlin("jvm") version "1.2.31" 48 | } 49 | 50 | application { 51 | if (SystemInfo.isMac) 52 | applicationDefaultJvmArgs = arrayListOf("-Xdock:name=Dev-Kt") 53 | mainClassName = "devkt.DevKtCompiledKt" 54 | } 55 | 56 | apply { 57 | plugin("kotlin") 58 | } 59 | 60 | java { 61 | sourceCompatibility = JavaVersion.VERSION_1_8 62 | targetCompatibility = JavaVersion.VERSION_1_8 63 | } 64 | 65 | tasks.withType { 66 | kotlinOptions { 67 | jvmTarget = "1.8" 68 | } 69 | } 70 | 71 | tasks.withType { 72 | manifest { 73 | attributes(mapOf("Main-Class" to application.mainClassName)) 74 | } 75 | } 76 | 77 | val fatJar = task("fatJar") { 78 | classifier = "all" 79 | description = "Assembles a jar archive containing the main classes and all the dependencies." 80 | group = "build" 81 | from(Callable { 82 | configurations.compile.map { 83 | @Suppress("IMPLICIT_CAST_TO_ANY") 84 | if (it.isDirectory) it else zipTree(it) 85 | } 86 | }) 87 | with(tasks["jar"] as Jar) 88 | } 89 | 90 | val SourceSet.kotlin 91 | get() = (this as HasConvention).convention.getPlugin(KotlinSourceSet::class.java).kotlin 92 | 93 | java.sourceSets { 94 | "main" { 95 | java.setSrcDirs(listOf("src")) 96 | kotlin.setSrcDirs(listOf("src")) 97 | resources.setSrcDirs(listOf("res")) 98 | } 99 | 100 | "test" { 101 | java.setSrcDirs(listOf("test")) 102 | kotlin.setSrcDirs(listOf("test")) 103 | } 104 | } 105 | 106 | repositories { 107 | mavenCentral() 108 | jcenter() 109 | maven("https://jitpack.io") 110 | } 111 | 112 | dependencies { 113 | compile(kotlin("stdlib-jdk8", kotlinVersion)) 114 | testCompile("junit", "junit", "4.12") 115 | testCompile(kotlin("test-junit", kotlinVersion)) 116 | testCompile(kotlin("stdlib-jdk8", kotlinVersion)) 117 | } 118 | -------------------------------------------------------------------------------- /common/test/org/ice1000/devkt/kts/utils.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.kts 2 | 3 | import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys 4 | import org.jetbrains.kotlin.cli.common.messages.* 5 | import org.jetbrains.kotlin.config.* 6 | import org.junit.Assert 7 | import java.io.ByteArrayOutputStream 8 | import java.io.PrintStream 9 | 10 | internal const val NUM_4_LINE = "num: 4" 11 | 12 | internal const val FIB_SCRIPT_OUTPUT_TAIL = """ 13 | fib(1)=1 14 | fib(0)=1 15 | fib(2)=2 16 | fib(1)=1 17 | fib(3)=3 18 | fib(1)=1 19 | fib(0)=1 20 | fib(2)=2 21 | fib(4)=5 22 | """ 23 | 24 | fun newConfiguration(): CompilerConfiguration { 25 | val configuration = CompilerConfiguration() 26 | configuration.put(CommonConfigurationKeys.MODULE_NAME, "www") 27 | if ("true" == System.getProperty("kotlin.ni")) { 28 | // Enable new inference for tests which do not declare their own language version settings 29 | configuration.languageVersionSettings = CompilerTestLanguageVersionSettings(emptyMap(), 30 | LanguageVersionSettingsImpl.DEFAULT.apiVersion, 31 | LanguageVersionSettingsImpl.DEFAULT.languageVersion, 32 | emptyMap()) 33 | } 34 | 35 | configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, object : MessageCollector { 36 | override fun clear() = Unit 37 | override fun hasErrors() = false 38 | override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageSourceLocation?) { 39 | if (severity == CompilerMessageSeverity.ERROR) { 40 | val prefix = if (location == null) "" else "(${location.path}:${location.line}:${location.column}) " 41 | throw AssertionError(prefix + message) 42 | } 43 | } 44 | }) 45 | return configuration 46 | } 47 | 48 | internal fun captureOut(body: () -> Unit): String { 49 | val outStream = ByteArrayOutputStream() 50 | val prevOut = System.out 51 | System.setOut(PrintStream(outStream)) 52 | try { 53 | body() 54 | } finally { 55 | System.out.flush() 56 | System.setOut(prevOut) 57 | } 58 | return outStream.toString() 59 | } 60 | 61 | private fun String.linesSplitTrim() = 62 | split('\n', '\r').map(String::trim).filter(String::isNotBlank) 63 | 64 | internal fun assertEqualsTrimmed(expected: String, actual: String) = 65 | Assert.assertEquals(expected.linesSplitTrim(), actual.linesSplitTrim()) 66 | 67 | data class CompilerTestLanguageVersionSettings( 68 | private val initialLanguageFeatures: Map, 69 | override val apiVersion: ApiVersion, 70 | override val languageVersion: LanguageVersion, 71 | private val analysisFlags: Map, Any?> = emptyMap() 72 | ) : LanguageVersionSettings { 73 | private val languageFeatures = initialLanguageFeatures + specificFeaturesForTests() 74 | private val delegate = LanguageVersionSettingsImpl(languageVersion, apiVersion) 75 | 76 | override fun isPreRelease() = true 77 | 78 | override fun getFeatureSupport(feature: LanguageFeature): LanguageFeature.State = 79 | languageFeatures[feature] ?: delegate.getFeatureSupport(feature) 80 | 81 | @Suppress("UNCHECKED_CAST") 82 | override fun getFlag(flag: AnalysisFlag): T = analysisFlags[flag] as T? ?: flag.defaultValue 83 | } 84 | 85 | private fun specificFeaturesForTests(): Map { 86 | return if (System.getProperty("kotlin.ni") == "true") 87 | mapOf(LanguageFeature.NewInference to LanguageFeature.State.ENABLED) 88 | else 89 | emptyMap() 90 | } 91 | -------------------------------------------------------------------------------- /swing/src/ui/swing/dialogs/go-to-line.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.ui.swing.dialogs 2 | 3 | import com.intellij.uiDesigner.core.GridConstraints 4 | import com.intellij.uiDesigner.core.GridLayoutManager 5 | import com.intellij.uiDesigner.core.Spacer 6 | import org.ice1000.devkt.ui.DevKtDocument 7 | import org.ice1000.devkt.ui.swing.AbstractUI 8 | import java.awt.Dimension 9 | import java.awt.Insets 10 | import javax.swing.* 11 | 12 | class GoToLineDialog(uiImpl: AbstractUI, private val document: DevKtDocument<*>) : JDialog() { 13 | private val mainPanel = JPanel() 14 | private val lineColumn = JTextField() 15 | private val cancelButton = JButton() 16 | private val okButton = JButton() 17 | 18 | init { 19 | mainPanel.layout = GridLayoutManager(2, 3, Insets(8, 8, 8, 8), -1, -1) 20 | val panel1 = JPanel() 21 | panel1.layout = GridLayoutManager(2, 2, Insets(0, 0, 0, 0), -1, -1) 22 | mainPanel.add(panel1, GridConstraints(0, 0, 1, 3, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)) 23 | panel1.add(lineColumn, GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, Dimension(150, -1), null, 0, false)) 24 | val label1 = JLabel() 25 | label1.text = "[Line] [:column]:" 26 | panel1.add(label1, GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)) 27 | val label2 = JLabel() 28 | label2.text = "Format: \"line\" or \"line:column\"" 29 | panel1.add(label2, GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)) 30 | cancelButton.text = "Cancel" 31 | mainPanel.add(cancelButton, GridConstraints(1, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)) 32 | val spacer1 = Spacer() 33 | mainPanel.add(spacer1, GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)) 34 | okButton.text = "OK" 35 | mainPanel.add(okButton, GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)) 36 | label1.labelFor = lineColumn 37 | 38 | setLocationRelativeTo(uiImpl.mainPanel) 39 | getRootPane().defaultButton = okButton 40 | contentPane = mainPanel 41 | title = "Go to Line/Column" 42 | isModal = true 43 | pack() 44 | okButton.addActionListener { ok() } 45 | cancelButton.addActionListener { dispose() } 46 | lineColumn.text = document.posToLineColumn(document.caretPosition).let { (line, column) -> 47 | "$line:$column" 48 | } 49 | } 50 | 51 | private fun ok() { 52 | val input = lineColumn.text.split(':') 53 | val line = input.firstOrNull()?.toIntOrNull() ?: return 54 | val column = input.getOrNull(1)?.toIntOrNull() ?: 1 55 | document.caretPosition = document.lineColumnToPos(line, column) 56 | dispose() 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /swing/src/ui/swing/forms/GoToLine.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 |
70 | -------------------------------------------------------------------------------- /common/src/org/ice1000/devkt/openapi/highlight.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.openapi 2 | 3 | import org.ice1000.devkt.config.GlobalSettings 4 | import org.jetbrains.kotlin.com.intellij.lang.ASTNode 5 | import org.jetbrains.kotlin.com.intellij.openapi.util.TextRange 6 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 7 | import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType 8 | 9 | /** 10 | * @author ice1000 11 | */ 12 | interface AnnotationHolder : LengthOwner { 13 | val text: String 14 | fun highlight(tokenStart: Int, tokenEnd: Int, attributeSet: TextAttributes) 15 | 16 | fun highlight(range: TextRange, attributeSet: TextAttributes) = 17 | highlight(range.startOffset, range.endOffset, attributeSet) 18 | 19 | fun highlight(astNode: ASTNode, attributeSet: TextAttributes) = 20 | highlight(astNode.textRange, attributeSet) 21 | 22 | fun highlight(element: PsiElement, attributeSet: TextAttributes) = 23 | highlight(element.textRange, attributeSet) 24 | } 25 | 26 | /** 27 | * @author ice1000 28 | * @since v1.2 29 | * @see com.intellij.openapi.fileTypes.SyntaxHighlighter 30 | */ 31 | interface SyntaxHighlighter { 32 | /** 33 | * Highlight tokens. This is called after lexing. 34 | * 35 | * @param type IElementType token's type 36 | * @param colorScheme ColorScheme the holder of colors 37 | * @return TextAttributes? a member of [colorScheme] 38 | * @see com.intellij.openapi.fileTypes.SyntaxHighlighter.getTokenHighlights 39 | */ 40 | fun attributesOf(type: IElementType, colorScheme: ColorScheme): TextAttributes? 41 | } 42 | 43 | /** 44 | * @author ice1000 45 | * @since v0.0.1 46 | * @see com.intellij.lang.annotation.Annotator 47 | * TODO move to daemon instead of running in ui thread 48 | */ 49 | interface Annotator { 50 | /** 51 | * @param element the [PsiElement] to be highlighted 52 | * @param document similar to [com.intellij.lang.annotation.AnnotationHolder] 53 | * @param colorScheme current color scheme, initialized in [org.ice1000.devkt.config.GlobalSettings] 54 | * @see com.intellij.lang.annotation.Annotator.annotate 55 | */ 56 | fun annotate( 57 | element: PsiElement, 58 | document: AnnotationHolder, 59 | colorScheme: ColorScheme) 60 | } 61 | 62 | class ColorScheme( 63 | settings: GlobalSettings, 64 | val tabSize: TextAttributes, 65 | wrapColor: (String) -> TextAttributes) { 66 | val keywords = wrapColor(settings.colorKeywords) 67 | val predefined = wrapColor(settings.colorPredefined) 68 | val string = wrapColor(settings.colorString) 69 | val stringEscape = wrapColor(settings.colorStringEscape) 70 | val interpolation = wrapColor(settings.colorInterpolation) 71 | val templateEntries = wrapColor(settings.colorTemplateEntries) 72 | val charLiteral = wrapColor(settings.colorCharLiteral) 73 | val lineComments = wrapColor(settings.colorLineComments) 74 | val blockComments = wrapColor(settings.colorBlockComments) 75 | val docComments = wrapColor(settings.colorDocComments) 76 | val operators = wrapColor(settings.colorOperators) 77 | val parentheses = wrapColor(settings.colorParentheses) 78 | val braces = wrapColor(settings.colorBraces) 79 | val brackets = wrapColor(settings.colorBrackets) 80 | val semicolon = wrapColor(settings.colorSemicolon) 81 | val numbers = wrapColor(settings.colorNumbers) 82 | val identifiers = wrapColor(settings.colorIdentifiers) 83 | val annotations = wrapColor(settings.colorAnnotations) 84 | val colon = wrapColor(settings.colorColon) 85 | val comma = wrapColor(settings.colorComma) 86 | val variable = wrapColor(settings.colorVariable) 87 | val function = wrapColor(settings.colorFunction) 88 | val typeParam = wrapColor(settings.colorTypeParam) 89 | val unknown = wrapColor(settings.colorUnknown) 90 | val error = wrapColor(settings.colorError) 91 | val userTypeRef = wrapColor(settings.colorUserTypeRef) 92 | val property = wrapColor(settings.colorProperty) 93 | val namespace = wrapColor(settings.colorNamespace) 94 | val metaData = wrapColor(settings.colorMetaData) 95 | val macro = wrapColor(settings.colorMacro) 96 | val default = wrapColor(settings.colorDefault) 97 | } 98 | 99 | -------------------------------------------------------------------------------- /swing/src/ui/swing/utils.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.ui.swing 2 | 3 | import org.ice1000.devkt.config.Key 4 | import org.ice1000.devkt.config.ShortCut 5 | import org.ice1000.devkt.openapi.util.CompletionElement 6 | import org.ice1000.devkt.openapi.util.CompletionPopup 7 | import org.ice1000.devkt.ui.MessageType 8 | import java.awt.event.KeyEvent 9 | import javax.swing.* 10 | 11 | /** 12 | * Ctrl for Windows/Linux, Meta for MacOS 13 | * Replacement of [java.awt.Toolkit.getMenuShortcutKeyMask] 14 | * @param key like [KeyEvent.VK_S] 15 | */ 16 | fun JMenuItem.keyMap(key: Int, modifiers: Int) { 17 | if (key != 0 && modifiers != 0) 18 | accelerator = KeyStroke.getKeyStroke(key, modifiers) 19 | } 20 | 21 | fun JMenuItem.keyMap(shortcut: ShortCut) = keyMap(shortcut.key.awt, shortcut.modifier) 22 | 23 | /** 24 | * @author ice1000 25 | * @property popup Popup swing popup 26 | * @property component JList the jList inside 27 | * @since v1.4 28 | */ 29 | class SwingPopup( 30 | private val popup: Popup, 31 | private val component: JList) : CompletionPopup { 32 | override fun hide() = popup.hide() 33 | override fun show() { 34 | popup.show() 35 | component.requestFocusInWindow() 36 | } 37 | 38 | override fun updateItems(completionElement: Collection) { 39 | component.model = ListListModel(completionElement) 40 | } 41 | } 42 | 43 | /** 44 | * Fuck Swing 45 | * 46 | * @author ice1000 47 | * @param E type of the data 48 | * @property list List the data list 49 | * @constructor construct from a list 50 | * @since v1.4 51 | */ 52 | class ListListModel(private val list: List) : AbstractListModel() { 53 | constructor(list: Collection) : this(list.toList()) 54 | 55 | override fun getSize() = list.size 56 | override fun getElementAt(var1x: Int) = list[var1x] 57 | } 58 | 59 | /** 60 | * @author ice1000 61 | * @since v1.4 62 | */ 63 | val MessageType.swing 64 | get() = when (this) { 65 | MessageType.Error -> JOptionPane.ERROR_MESSAGE 66 | MessageType.Information -> JOptionPane.INFORMATION_MESSAGE 67 | MessageType.Plain -> JOptionPane.PLAIN_MESSAGE 68 | MessageType.Question -> JOptionPane.QUESTION_MESSAGE 69 | MessageType.Warning -> JOptionPane.WARNING_MESSAGE 70 | } 71 | 72 | /** 73 | * @author ice1000 74 | * @since v1.3 75 | */ 76 | val Key.awt 77 | get() = when (this) { 78 | Key.A -> KeyEvent.VK_A 79 | Key.B -> KeyEvent.VK_B 80 | Key.C -> KeyEvent.VK_C 81 | Key.D -> KeyEvent.VK_D 82 | Key.E -> KeyEvent.VK_E 83 | Key.F -> KeyEvent.VK_F 84 | Key.G -> KeyEvent.VK_G 85 | Key.H -> KeyEvent.VK_H 86 | Key.I -> KeyEvent.VK_I 87 | Key.J -> KeyEvent.VK_J 88 | Key.K -> KeyEvent.VK_K 89 | Key.L -> KeyEvent.VK_L 90 | Key.M -> KeyEvent.VK_M 91 | Key.N -> KeyEvent.VK_N 92 | Key.O -> KeyEvent.VK_O 93 | Key.P -> KeyEvent.VK_P 94 | Key.Q -> KeyEvent.VK_Q 95 | Key.R -> KeyEvent.VK_R 96 | Key.S -> KeyEvent.VK_S 97 | Key.T -> KeyEvent.VK_T 98 | Key.U -> KeyEvent.VK_U 99 | Key.V -> KeyEvent.VK_V 100 | Key.W -> KeyEvent.VK_W 101 | Key.X -> KeyEvent.VK_X 102 | Key.Y -> KeyEvent.VK_Y 103 | Key.Z -> KeyEvent.VK_Z 104 | Key.`0` -> KeyEvent.VK_0 105 | Key.`1` -> KeyEvent.VK_1 106 | Key.`2` -> KeyEvent.VK_2 107 | Key.`3` -> KeyEvent.VK_3 108 | Key.`4` -> KeyEvent.VK_4 109 | Key.`5` -> KeyEvent.VK_5 110 | Key.`6` -> KeyEvent.VK_6 111 | Key.`7` -> KeyEvent.VK_7 112 | Key.`8` -> KeyEvent.VK_8 113 | Key.`9` -> KeyEvent.VK_9 114 | Key.F1 -> KeyEvent.VK_F1 115 | Key.F2 -> KeyEvent.VK_F2 116 | Key.F3 -> KeyEvent.VK_F3 117 | Key.F4 -> KeyEvent.VK_F4 118 | Key.F5 -> KeyEvent.VK_F5 119 | Key.F6 -> KeyEvent.VK_F6 120 | Key.F7 -> KeyEvent.VK_F7 121 | Key.F8 -> KeyEvent.VK_F8 122 | Key.F9 -> KeyEvent.VK_F9 123 | Key.F10 -> KeyEvent.VK_F10 124 | Key.F11 -> KeyEvent.VK_F11 125 | Key.F12 -> KeyEvent.VK_F12 126 | Key.SLASH -> KeyEvent.VK_SLASH 127 | Key.SPACE -> KeyEvent.VK_SPACE 128 | Key.ENTER -> KeyEvent.VK_ENTER 129 | Key.UP -> KeyEvent.VK_UP 130 | Key.DOWN -> KeyEvent.VK_DOWN 131 | Key.LEFT -> KeyEvent.VK_LEFT 132 | Key.RIGHT -> KeyEvent.VK_RIGHT 133 | } 134 | -------------------------------------------------------------------------------- /common/test/org/ice1000/devkt/kts/kts-test.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.kts 2 | 3 | //import org.ice1000.devkt.Analyzer 4 | //import org.ice1000.devkt.config.GlobalSettings 5 | //import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys 6 | //import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot 7 | //import org.jetbrains.kotlin.cli.common.messages.* 8 | //import org.jetbrains.kotlin.cli.jvm.compiler.* 9 | //import org.jetbrains.kotlin.codegen.CompilationException 10 | //import org.jetbrains.kotlin.com.intellij.openapi.util.Disposer 11 | //import org.jetbrains.kotlin.config.JVMConfigurationKeys 12 | //import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition 13 | //import org.jetbrains.kotlin.scripting.definitions.StandardScriptDefinition 14 | //import org.jetbrains.kotlin.utils.tryConstructClassFromStringArgs 15 | //import org.junit.Assert 16 | //import org.junit.Assert.assertEquals 17 | //import org.junit.Ignore 18 | //import java.io.File 19 | //import kotlin.script.templates.ScriptTemplateDefinition 20 | // 21 | //@ScriptTemplateDefinition 22 | //open class AbstractScriptTemplate(val args: Array) 23 | // 24 | //@ScriptTemplateDefinition 25 | //open class ScriptTemplate(args: Array) : AbstractScriptTemplate(args) 26 | // 27 | //@Ignore 28 | //class ScriptTest { 29 | // fun standardScriptWithParams() { 30 | // val aClass = compileScript("die_home_guy_so_disgusting.kts", StandardScriptDefinition) 31 | // Assert.assertNotNull(aClass) 32 | // val out = captureOut { 33 | // val anObj = tryConstructClassFromStringArgs(aClass!!, listOf("4", "commentCurrent")) 34 | // Assert.assertNotNull(anObj) 35 | // } 36 | // // assertEqualsTrimmed("$NUM_4_LINE (commentCurrent)$FIB_SCRIPT_OUTPUT_TAIL", out) 37 | // } 38 | // 39 | // fun standardScriptWithoutParams() { 40 | // val aClass = compileScript("die_home_guy_so_disgusting.kts", 41 | // KotlinScriptDefinition(ScriptTemplate::class))!! 42 | // val out = captureOut { 43 | // val anObj = tryConstructClassFromStringArgs(aClass, emptyList()) 44 | // Assert.assertNotNull(anObj) 45 | // } 46 | // assertEqualsTrimmed("我永远喜欢灵乌路空", out) 47 | // } 48 | // 49 | // fun useCompilerInternals() { 50 | // val scriptClass = compileScript("use_compiler_internals.kts", 51 | // KotlinScriptDefinition(ScriptTemplate::class), false)!! 52 | // assertEquals("OK", captureOut { 53 | // tryConstructClassFromStringArgs(scriptClass, emptyList()) 54 | // }) 55 | // } 56 | // 57 | // private fun compileScript( 58 | // scriptPath: String, 59 | // scriptDefinition: KotlinScriptDefinition, 60 | // runIsolated: Boolean = true, 61 | // suppressOutput: Boolean = false, 62 | // saveClassesDir: File? = null 63 | // ): Class<*>? { 64 | // val messageCollector = 65 | // if (suppressOutput) MessageCollector.NONE 66 | // else PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, false) 67 | // val rootDisposable = Disposer.newDisposable() 68 | // try { 69 | // val configuration = newConfiguration() 70 | // configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector) 71 | // configuration.addKotlinSourceRoot("testRes/script/$scriptPath") 72 | // // configuration.add(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition) 73 | // configuration.put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true) 74 | // if (saveClassesDir != null) configuration.put(JVMConfigurationKeys.OUTPUT_DIRECTORY, saveClassesDir) 75 | // val environment = KotlinCoreEnvironment.createForTests(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES) 76 | // return try { 77 | // TODO() 78 | // // KotlinToJVMBytecodeCompiler.compileScript(environment, javaClass.classLoader.takeUnless { runIsolated }) 79 | // } catch (e: CompilationException) { 80 | // messageCollector.report(CompilerMessageSeverity.EXCEPTION, OutputMessageUtil.renderException(e), 81 | // MessageUtil.psiElementToMessageLocation(e.element)) 82 | // null 83 | // } catch (t: Throwable) { 84 | // MessageCollectorUtil.reportException(messageCollector, t) 85 | // throw t 86 | // } 87 | // } finally { 88 | // Disposer.dispose(rootDisposable) 89 | // } 90 | // } 91 | //} 92 | // 93 | //fun main(args: Array) { 94 | // val file = File("res/template/script.kts") 95 | // GlobalSettings.load() 96 | // Analyzer.runScript(file.readText()) 97 | //} 98 | // 99 | // 100 | -------------------------------------------------------------------------------- /common/src/org/jetbrains/kotlin/com/intellij/util/containers/IdeaQueue.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2013 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:Suppress("UNCHECKED_CAST", "unused") 17 | 18 | package org.jetbrains.kotlin.com.intellij.util.containers 19 | 20 | import org.jetbrains.kotlin.com.intellij.util.ArrayUtil 21 | import java.util.* 22 | 23 | /** 24 | * Renamed to [IdeaQueue] to avoid conflicting with [Queue] which is insufficient 25 | * for providing full support for [)][org.jetbrains.kotlin.com.intellij.lexer.LookAheadLexer] 26 | * 27 | * @param 28 | */ 29 | class IdeaQueue(initialCapacity: Int) { 30 | private var myArray: Array = if (initialCapacity > 0) arrayOfNulls(initialCapacity) else ArrayUtil.EMPTY_OBJECT_ARRAY 31 | private var myFirst: Int = 0 32 | private var myLast: Int = 0 33 | // if true, elements are located at myFirst..myArray.length and 0..myLast 34 | // otherwise, they are at myFirst..myLast 35 | private var isWrapped: Boolean = false 36 | val isEmpty: Boolean get() = size() == 0 37 | 38 | fun addLast(`object`: T) { 39 | val currentSize = size() 40 | if (currentSize == myArray.size) { 41 | myArray = normalize(Math.max(currentSize * 3 / 2, 10)) 42 | myFirst = 0 43 | myLast = currentSize 44 | isWrapped = false 45 | } 46 | myArray[myLast] = `object` 47 | myLast++ 48 | if (myLast == myArray.size) { 49 | isWrapped = !isWrapped 50 | myLast = 0 51 | } 52 | } 53 | 54 | fun removeLast(): T { 55 | if (myLast == 0) { 56 | isWrapped = !isWrapped 57 | myLast = myArray.size 58 | } 59 | myLast-- 60 | val result = myArray[myLast] as T 61 | myArray[myLast] = null 62 | return result 63 | } 64 | 65 | fun peekLast(): T { 66 | var last = myLast 67 | if (last == 0) last = myArray.size 68 | return myArray[last - 1] as T 69 | } 70 | 71 | fun size() = if (isWrapped) myArray.size - myFirst + myLast else myLast - myFirst 72 | fun toList() = normalize(size()).toList() as List 73 | fun toArray() = normalize(size()) 74 | 75 | fun pullFirst(): T { 76 | val result = peekFirst() 77 | myArray[myFirst] = null 78 | myFirst++ 79 | if (myFirst == myArray.size) { 80 | myFirst = 0 81 | isWrapped = !isWrapped 82 | } 83 | return result 84 | } 85 | 86 | fun peekFirst(): T { 87 | if (isEmpty) throw IndexOutOfBoundsException("queue is empty") 88 | return myArray[myFirst] as T 89 | } 90 | 91 | private fun copyFromTo(first: Int, last: Int, result: Array, destinationPos: Int): Int { 92 | val length = last - first 93 | System.arraycopy(myArray, first, result, destinationPos, length) 94 | return length 95 | } 96 | 97 | private fun normalize(capacity: Int) = normalize(arrayOfNulls(capacity)) 98 | private fun normalize(result: Array): Array { 99 | if (isWrapped) { 100 | val tailLength = copyFromTo(myFirst, myArray.size, result, 0) 101 | copyFromTo(0, myLast, result, tailLength) 102 | } else copyFromTo(myFirst, myLast, result, 0) 103 | return result 104 | } 105 | 106 | fun clear() { 107 | Arrays.fill(myArray, null) 108 | myLast = 0 109 | myFirst = myLast 110 | isWrapped = false 111 | } 112 | 113 | operator fun set(index: Int, value: T): T { 114 | var arrayIndex = myFirst + index 115 | if (isWrapped && arrayIndex >= myArray.size) arrayIndex -= myArray.size 116 | val old = myArray[arrayIndex] 117 | myArray[arrayIndex] = value 118 | return old as T 119 | } 120 | 121 | operator fun get(index: Int): T { 122 | var arrayIndex = myFirst + index 123 | if (isWrapped && arrayIndex >= myArray.size) arrayIndex -= myArray.size 124 | return myArray[arrayIndex] as T 125 | } 126 | 127 | override fun toString() = when { 128 | isEmpty -> "" 129 | isWrapped -> "[ ${sub(myFirst, myArray.size)} ||| ${sub(0, myLast)} ]" 130 | else -> "[ ${sub(myFirst, myLast)} ]" 131 | } 132 | 133 | private fun sub(start: Int, end: Int): Any = if (start == end) "" else Arrays.asList(*myArray).subList(start, end) 134 | } 135 | -------------------------------------------------------------------------------- /swing/src/ui/swing/dialogs/psi-viewer.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.ui.swing.dialogs 2 | 3 | import com.intellij.uiDesigner.core.GridConstraints 4 | import com.intellij.uiDesigner.core.GridLayoutManager 5 | import com.intellij.uiDesigner.core.Spacer 6 | import org.ice1000.devkt.config.GlobalSettings 7 | import org.ice1000.devkt.openapi.nodeType 8 | import org.ice1000.devkt.openapi.util.cutText 9 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 10 | import org.jetbrains.kotlin.com.intellij.psi.PsiFile 11 | import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace 12 | import java.awt.Insets 13 | import java.awt.Window 14 | import javax.swing.* 15 | import javax.swing.tree.DefaultMutableTreeNode 16 | import javax.swing.tree.TreeNode 17 | import javax.swing.tree.TreePath 18 | 19 | typealias UINode = DefaultMutableTreeNode 20 | 21 | /** 22 | * @author ice1000 23 | */ 24 | class PsiViewerImpl(file: PsiFile, owner: Window? = null) : JDialog(owner) { 25 | private val mainPanel = JPanel() 26 | private val buttonClose = JButton() 27 | private val pane = JScrollPane() 28 | private val expandAll = JButton() 29 | private val collapseAll = JButton() 30 | 31 | init { 32 | mainPanel.layout = GridLayoutManager(2, 4, Insets(10, 10, 10, 10), -1, -1) 33 | mainPanel.add(pane, GridConstraints(0, 0, 1, 4, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)) 34 | val spacer1 = Spacer() 35 | mainPanel.add(spacer1, GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)) 36 | buttonClose.text = "Close" 37 | buttonClose.setMnemonic('C') 38 | buttonClose.displayedMnemonicIndex = 0 39 | mainPanel.add(buttonClose, GridConstraints(1, 3, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)) 40 | expandAll.text = "Expand all" 41 | expandAll.setMnemonic('E') 42 | expandAll.displayedMnemonicIndex = 0 43 | mainPanel.add(expandAll, GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)) 44 | collapseAll.text = "Collapse all" 45 | collapseAll.setMnemonic('A') 46 | collapseAll.displayedMnemonicIndex = 9 47 | mainPanel.add(collapseAll, GridConstraints(1, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)) 48 | 49 | contentPane = mainPanel 50 | if (owner != null) setLocationRelativeTo(owner) 51 | title = "Psi Viewer" 52 | isModal = true 53 | rootPane.defaultButton = buttonClose 54 | val tree = JTree(mapAst2Display(file)) 55 | pane.setViewportView(tree) 56 | expandAll.addActionListener { expandAll(tree, true) } 57 | collapseAll.addActionListener { expandAll(tree, false) } 58 | buttonClose.addActionListener { dispose() } 59 | pack() 60 | } 61 | 62 | /** 63 | * 缅怀一下天国的 Lice AST Viewer 64 | */ 65 | private fun mapAst2Display( 66 | node: PsiElement, 67 | root: UINode = UINode(prettyPrint(node)) 68 | ): UINode = when { 69 | node.firstChild == null -> UINode(prettyPrint(node)) 70 | else -> root.apply { 71 | generateSequence(node.firstChild) { 72 | it.nextSibling 73 | }.forEach { if (it !is PsiWhiteSpace) add(mapAst2Display(it)) } 74 | } 75 | } 76 | 77 | private fun expandAll(tree: JTree, expand: Boolean) { 78 | val root = tree.model.root as TreeNode 79 | expandAll(tree, TreePath(root), expand) 80 | } 81 | 82 | /** 83 | * @return Whether an expandPath was called for the last node in the parent path 84 | */ 85 | private fun expandAll(tree: JTree, parent: TreePath, expand: Boolean): Boolean { 86 | val node = parent.lastPathComponent as TreeNode 87 | return if (node.childCount > 0) { 88 | var childExpandCalled = false 89 | val e = node.children() 90 | while (e.hasMoreElements()) { 91 | val n = e.nextElement() as TreeNode 92 | val path = parent.pathByAddingChild(n) 93 | childExpandCalled = expandAll(tree, path, expand) || childExpandCalled 94 | } 95 | 96 | if (!childExpandCalled) { 97 | if (expand) tree.expandPath(parent) else tree.collapsePath(parent) 98 | } 99 | true 100 | } else false 101 | } 102 | 103 | 104 | private fun prettyPrint(node: PsiElement) = 105 | "${cutText(node.text, GlobalSettings.psiViewerMaxCodeLength)} => ${node.javaClass.simpleName}(${node.nodeType})" 106 | } 107 | -------------------------------------------------------------------------------- /swing/src/ui/swing/forms/UI.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /common/src/org/jetbrains/kotlin/com/intellij/lexer/LookAheadLexer.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2014 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:Suppress("MemberVisibilityCanBePrivate") 17 | 18 | package org.jetbrains.kotlin.com.intellij.lexer; 19 | 20 | import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType 21 | import org.jetbrains.kotlin.com.intellij.util.containers.IdeaQueue 22 | import org.jetbrains.kotlin.com.intellij.util.containers.ImmutableUserMap 23 | 24 | /** 25 | * @author peter 26 | */ 27 | abstract class LookAheadLexer @JvmOverloads constructor(private val myBaseLexer: Lexer, capacity: Int = 64) : LexerBase() { 28 | private var myLastOffset: Int = 0 29 | private var myLastState: Int = 0 30 | private var myTokenStart: Int = 0 31 | private val myTypeCache: IdeaQueue = IdeaQueue(capacity) 32 | private val myEndOffsetCache: IdeaQueue = IdeaQueue(capacity) 33 | 34 | protected open val cacheSize: Int 35 | get() = myTypeCache.size() 36 | 37 | protected open fun addToken(type: IElementType?) { 38 | addToken(myBaseLexer.tokenEnd, type) 39 | } 40 | 41 | protected open fun addToken(endOffset: Int, type: IElementType?) { 42 | myTypeCache.addLast(type) 43 | myEndOffsetCache.addLast(endOffset) 44 | } 45 | 46 | protected open fun lookAhead(baseLexer: Lexer) { 47 | advanceLexer(baseLexer) 48 | } 49 | 50 | override fun advance() { 51 | if (!myTypeCache.isEmpty) { 52 | myTypeCache.pullFirst() 53 | myTokenStart = myEndOffsetCache.pullFirst() 54 | } 55 | if (myTypeCache.isEmpty) doLookAhead() 56 | } 57 | 58 | private fun doLookAhead() { 59 | myLastOffset = myTokenStart 60 | myLastState = myBaseLexer.state 61 | 62 | lookAhead(myBaseLexer) 63 | assert(!myTypeCache.isEmpty) 64 | } 65 | 66 | override fun getBufferSequence(): CharSequence { 67 | return myBaseLexer.bufferSequence 68 | } 69 | 70 | override fun getBufferEnd(): Int { 71 | return myBaseLexer.bufferEnd 72 | } 73 | 74 | protected open fun resetCacheSize(size: Int) { 75 | while (myTypeCache.size() > size) { 76 | myTypeCache.removeLast() 77 | myEndOffsetCache.removeLast() 78 | } 79 | } 80 | 81 | open fun replaceCachedType(index: Int, token: IElementType): IElementType? { 82 | return myTypeCache.set(index, token) 83 | } 84 | 85 | protected open fun getCachedType(index: Int): IElementType? { 86 | return myTypeCache[index] 87 | } 88 | 89 | protected open fun getCachedOffset(index: Int): Int { 90 | return myEndOffsetCache[index] 91 | } 92 | 93 | override fun getState(): Int { 94 | val offset = myTokenStart - myLastOffset 95 | return myLastState or (offset shl 16) 96 | } 97 | 98 | override fun getTokenEnd(): Int { 99 | return myEndOffsetCache.peekFirst() 100 | } 101 | 102 | override fun getTokenStart(): Int { 103 | return myTokenStart 104 | } 105 | 106 | override fun getCurrentPosition(): LookAheadLexerPosition { 107 | return LookAheadLexerPosition(this, ImmutableUserMap.EMPTY) 108 | } 109 | 110 | override fun restore(position: LexerPosition) { 111 | restore(position as LookAheadLexerPosition) 112 | } 113 | 114 | protected open fun restore(position: LookAheadLexerPosition) { 115 | start(myBaseLexer.bufferSequence, position.lastOffset, myBaseLexer.bufferEnd, position.lastState) 116 | for (i in 0 until position.advanceCount) { 117 | advance() 118 | } 119 | } 120 | 121 | override fun getTokenType() = myTypeCache.peekFirst() 122 | 123 | override fun start(buffer: CharSequence, startOffset: Int, endOffset: Int, initialState: Int) { 124 | myBaseLexer.start(buffer, startOffset, endOffset, initialState and 0xFFFF) 125 | myTokenStart = startOffset 126 | myTypeCache.clear() 127 | myEndOffsetCache.clear() 128 | doLookAhead() 129 | } 130 | 131 | class LookAheadLexerPosition(lookAheadLexer: LookAheadLexer, val customMap: ImmutableUserMap) : LexerPosition { 132 | internal val lastOffset: Int = lookAheadLexer.myLastOffset 133 | internal val lastState: Int = lookAheadLexer.myLastState 134 | internal val tokenStart: Int = lookAheadLexer.myTokenStart 135 | internal val advanceCount: Int = lookAheadLexer.myTypeCache.size() - 1 136 | 137 | override fun getOffset() = tokenStart 138 | override fun getState() = lastState 139 | } 140 | 141 | protected open fun advanceLexer(lexer: Lexer) { 142 | advanceAs(lexer, lexer.tokenType) 143 | } 144 | 145 | protected open fun advanceAs(lexer: Lexer, type: IElementType?) { 146 | addToken(type) 147 | lexer.advance() 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /common/src/org/ice1000/devkt/openapi/ui/ui.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.openapi.ui 2 | 3 | import org.ice1000.devkt.ASTToken 4 | import org.ice1000.devkt.lang.DevKtLanguage 5 | import org.ice1000.devkt.openapi.AnnotationHolder 6 | import org.ice1000.devkt.openapi.LengthOwner 7 | import org.ice1000.devkt.ui.Edit 8 | import java.net.URL 9 | import javax.swing.Icon 10 | import com.bulenkov.iconloader.IconLoader as JetBrainsIconLoader 11 | 12 | /** 13 | * @author ice1000 14 | * @since v1.3 15 | * @param TextAttributes 16 | * @property selectionStart Int 17 | * @property selectionEnd Int 18 | * @property canUndo Boolean 19 | * @property canRedo Boolean 20 | */ 21 | interface IDevKtDocumentHandler : AnnotationHolder { 22 | var selectionStart: Int 23 | var selectionEnd: Int 24 | val canUndo: Boolean 25 | val canRedo: Boolean 26 | val document: IDevKtDocument 27 | val currentTypingNode: ASTToken? 28 | fun startOffsetOf(line: Int): Int 29 | fun endOffsetOf(line: Int): Int 30 | fun lineOf(offset: Int): Int 31 | fun textWithin(start: Int, end: Int): String 32 | fun replaceText(regex: Regex, replacement: String): String 33 | fun undo() 34 | fun redo() 35 | fun done() 36 | fun clearUndo() 37 | fun addEdit(offset: Int, text: CharSequence, isInsert: Boolean) 38 | fun addEdit(edit: Edit) 39 | fun switchLanguage(fileName: String) 40 | fun switchLanguage(language: DevKtLanguage) 41 | fun adjustFormat(offs: Int = 0, len: Int = length - offs) 42 | /** 43 | * Delete without checking 44 | * 45 | * @param offset Int see [delete] 46 | * @param length Int see [delete] 47 | */ 48 | fun deleteDirectly(offset: Int, length: Int, reparse: Boolean = true) 49 | 50 | /** 51 | * Handles user input, delete with checks and undo recording 52 | * 53 | * @param offs Int see [insert] 54 | * @param len Int length of deletion 55 | */ 56 | fun delete(offs: Int, len: Int) 57 | 58 | /** 59 | * Delete before current caret with checks and undo recording 60 | * 61 | * @param len Int length of deletion 62 | */ 63 | @JvmDefault 64 | fun delete(len: Int) = delete(document.caretPosition, len) 65 | 66 | /** 67 | * Delete at current caret with checks and undo recording 68 | * 69 | * @param len Int length of deletion 70 | */ 71 | @JvmDefault 72 | fun backSpace(len: Int) = delete(document.caretPosition - 1, len) 73 | 74 | /** 75 | * Clear the editor and set the content with undo recording 76 | * 77 | * @param string String new content. 78 | */ 79 | fun resetTextTo(string: String) 80 | 81 | /** 82 | * Insert without checking 83 | * 84 | * @param offset Int see [insert] 85 | * @param string String see [insert] 86 | * @param move Int how long the caret should move 87 | */ 88 | fun insertDirectly(offset: Int, string: String, move: Int = 0, reparse: Boolean = true) 89 | 90 | /** 91 | * Handles user input, insert with checks and undo recording 92 | * 93 | * @param offs Int offset from the beginning of the document 94 | * @param str String? text to insert 95 | */ 96 | fun insert(offs: Int, str: String?) 97 | 98 | /** 99 | * Insert at current caret position with checks and undo recording 100 | * 101 | * @param str String? text to insert 102 | */ 103 | @JvmDefault 104 | fun insert(str: String?) = insert(document.caretPosition, str) 105 | 106 | fun reparse(rehighlight: Boolean = true) 107 | } 108 | 109 | interface IDevKtDocument : LengthOwner { 110 | var caretPosition: Int 111 | var selectionStart: Int 112 | var selectionEnd: Int 113 | fun clear() 114 | fun delete(offs: Int, len: Int) 115 | fun insert(offs: Int, str: String?) 116 | fun changeCharacterAttributes(offset: Int, length: Int, s: TextAttributes, replace: Boolean) 117 | fun changeParagraphAttributes(offset: Int, length: Int, s: TextAttributes, replace: Boolean) 118 | fun resetLineNumberLabel(str: String) 119 | fun onChangeLanguage(newLanguage: DevKtLanguage) 120 | fun startOffsetOf(line: Int): Int 121 | fun endOffsetOf(line: Int): Int 122 | fun lineOf(offset: Int): Int 123 | fun lockWrite() 124 | fun unlockWrite() 125 | } 126 | 127 | /** 128 | * @author ice1000 129 | * @since v1.4 130 | */ 131 | object IconLoader { 132 | @JvmStatic 133 | fun getIcon(path: String) = JetBrainsIconLoader.getIcon(path) 134 | 135 | @JvmStatic 136 | fun getIcon(path: String, `class`: Class<*>) = JetBrainsIconLoader.getIcon(path, `class`) 137 | 138 | @JvmStatic 139 | fun getTransparentIcon(icon: Icon) = JetBrainsIconLoader.getTransparentIcon(icon) 140 | 141 | @JvmStatic 142 | fun getTransparentIcon(icon: Icon, alpha: Float) = JetBrainsIconLoader.getTransparentIcon(icon, alpha) 143 | 144 | @JvmStatic 145 | fun findIcon(path: String) = JetBrainsIconLoader.findIcon(path) 146 | 147 | @JvmStatic 148 | fun findIcon(path: String, `class`: Class<*>) = JetBrainsIconLoader.findIcon(path, `class`) 149 | 150 | @JvmStatic 151 | fun findIcon(path: URL) = JetBrainsIconLoader.findIcon(path) 152 | 153 | @JvmStatic 154 | fun findIcon(path: URL, useCache: Boolean) = JetBrainsIconLoader.findIcon(path, useCache) 155 | } 156 | -------------------------------------------------------------------------------- /common/src/org/ice1000/devkt/lang/annotators.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.lang 2 | 3 | import org.ice1000.devkt.openapi.* 4 | import org.jetbrains.kotlin.com.intellij.psi.* 5 | import org.jetbrains.kotlin.lexer.KtTokens 6 | import org.jetbrains.kotlin.psi.* 7 | import org.jetbrains.kotlin.psi.psiUtil.endOffset 8 | import org.jetbrains.kotlin.psi.psiUtil.startOffset 9 | 10 | /** 11 | * @author ice1000 12 | * @since v1.2 13 | */ 14 | class JavaAnnotator : Annotator { 15 | override fun annotate( 16 | element: PsiElement, 17 | document: AnnotationHolder, 18 | colorScheme: ColorScheme 19 | ) { 20 | when (element) { 21 | is PsiAnnotation -> annotation(element, document, colorScheme) 22 | is PsiTypeElement -> typeElement(element, document, colorScheme) 23 | is PsiMethod -> method(element, document, colorScheme) 24 | is PsiField -> field(element, document, colorScheme) 25 | is PsiVariable -> variable(element, document, colorScheme) 26 | is PsiErrorElement -> document.highlight(element, colorScheme.error) 27 | } 28 | } 29 | 30 | private fun variable( 31 | element: PsiVariable, 32 | document: AnnotationHolder, 33 | colorScheme: ColorScheme 34 | ) { 35 | element.nameIdentifier?.let { document.highlight(it, colorScheme.variable) } 36 | } 37 | 38 | private fun method( 39 | element: PsiMethod, 40 | document: AnnotationHolder, 41 | colorScheme: ColorScheme 42 | ) { 43 | element.nameIdentifier?.let { document.highlight(it, colorScheme.function) } 44 | } 45 | 46 | private fun field( 47 | element: PsiField, 48 | document: AnnotationHolder, 49 | colorScheme: ColorScheme 50 | ) { 51 | document.highlight(element.nameIdentifier, colorScheme.property) 52 | } 53 | 54 | private fun typeElement( 55 | element: PsiElement, 56 | document: AnnotationHolder, 57 | colorScheme: ColorScheme 58 | ) { 59 | if (element.firstChild !is PsiKeyword) 60 | document.highlight(element, colorScheme.userTypeRef) 61 | } 62 | 63 | private fun annotation( 64 | element: PsiAnnotation, 65 | document: AnnotationHolder, 66 | colorScheme: ColorScheme 67 | ) { 68 | val start = element.startOffset 69 | val end = element.nameReferenceElement?.endOffset ?: element.firstChild?.endOffset ?: start 70 | document.highlight(start, end, colorScheme.annotations) 71 | } 72 | } 73 | 74 | /** 75 | * @author ice1000 76 | * @since v0.0.1 77 | */ 78 | class KotlinAnnotator : Annotator { 79 | override fun annotate( 80 | element: PsiElement, 81 | document: AnnotationHolder, 82 | colorScheme: ColorScheme 83 | ) { 84 | if (element.nodeType in KtTokens.SOFT_KEYWORDS) { 85 | document.highlight(element, colorScheme.keywords) 86 | return 87 | } 88 | when (element) { 89 | is KtAnnotationEntry -> annotationEntry(element, document, colorScheme) 90 | is KtTypeParameter -> typeParameter(element, document, colorScheme) 91 | is KtTypeReference -> typeReference(element, document, colorScheme) 92 | is KtNamedFunction -> namedFunction(element, document, colorScheme) 93 | is KtProperty -> property(element, document, colorScheme) 94 | is PsiErrorElement -> document.highlight(element, colorScheme.error) 95 | } 96 | } 97 | 98 | private fun property( 99 | element: KtProperty, 100 | document: AnnotationHolder, 101 | colorScheme: ColorScheme 102 | ) { 103 | element.nameIdentifier?.let { 104 | document.highlight(it, colorScheme.property) 105 | } 106 | } 107 | 108 | private fun typeReference( 109 | element: KtTypeReference, 110 | document: AnnotationHolder, 111 | colorScheme: ColorScheme 112 | ) { 113 | if (element.parent !is KtConstructorCalleeExpression) 114 | document.highlight(element.firstChild 115 | ?.takeIf { it is KtUserType || it is KtNullableType } 116 | ?: return, colorScheme.userTypeRef) 117 | } 118 | 119 | private fun namedFunction( 120 | element: KtNamedFunction, 121 | document: AnnotationHolder, 122 | colorScheme: ColorScheme 123 | ) { 124 | element.nameIdentifier?.let { 125 | document.highlight(it, colorScheme.function) 126 | } 127 | } 128 | 129 | private fun typeParameter( 130 | element: KtTypeParameter, 131 | document: AnnotationHolder, 132 | colorScheme: ColorScheme 133 | ) { 134 | document.highlight(element, colorScheme.typeParam) 135 | element.references.forEach { 136 | document.highlight(it.element, colorScheme.typeParam) 137 | } 138 | } 139 | 140 | private fun annotationEntry( 141 | element: KtAnnotationEntry, 142 | document: AnnotationHolder, 143 | colorScheme: ColorScheme 144 | ) { 145 | val start = element.startOffset 146 | val end = element.typeReference?.endOffset ?: element.atSymbol?.endOffset ?: start 147 | document.highlight(start, end, colorScheme.annotations) 148 | } 149 | } 150 | 151 | /** 152 | * @author ice1000 153 | * @since v1.3 154 | */ 155 | class PlainTextAnnotator : Annotator { 156 | override fun annotate(element: PsiElement, document: AnnotationHolder, colorScheme: ColorScheme) { 157 | // Do nothing 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /common/src/org/ice1000/devkt/lang/highlighters.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.lang 2 | 3 | import org.ice1000.devkt.openapi.ColorScheme 4 | import org.ice1000.devkt.openapi.SyntaxHighlighter 5 | import org.ice1000.devkt.stringTemplateTokens 6 | import org.ice1000.devkt.stringTokens 7 | import org.jetbrains.kotlin.com.intellij.psi.JavaTokenType 8 | import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.JavaDocElementType 9 | import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType 10 | import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet 11 | import org.jetbrains.kotlin.lexer.KtTokens 12 | 13 | class KotlinSyntaxHighlighter : SyntaxHighlighter { 14 | override fun attributesOf(type: IElementType, colorScheme: ColorScheme) = when (type) { 15 | KtTokens.IDENTIFIER -> colorScheme.identifiers 16 | KtTokens.CHARACTER_LITERAL -> colorScheme.charLiteral 17 | KtTokens.EOL_COMMENT -> colorScheme.lineComments 18 | KtTokens.DOC_COMMENT -> colorScheme.docComments 19 | KtTokens.SEMICOLON -> colorScheme.semicolon 20 | KtTokens.COLON -> colorScheme.colon 21 | KtTokens.COMMA -> colorScheme.comma 22 | KtTokens.INTEGER_LITERAL, KtTokens.FLOAT_LITERAL -> colorScheme.numbers 23 | KtTokens.LPAR, KtTokens.RPAR -> colorScheme.parentheses 24 | KtTokens.LBRACE, KtTokens.RBRACE -> colorScheme.braces 25 | KtTokens.LBRACKET, KtTokens.RBRACKET -> colorScheme.brackets 26 | KtTokens.BLOCK_COMMENT, KtTokens.SHEBANG_COMMENT -> colorScheme.blockComments 27 | in stringTokens -> colorScheme.string 28 | in stringTemplateTokens -> colorScheme.templateEntries 29 | in KtTokens.KEYWORDS -> colorScheme.keywords 30 | in KtTokens.OPERATIONS -> colorScheme.operators 31 | else -> null 32 | } 33 | } 34 | 35 | class JavaSyntaxHighlighter : SyntaxHighlighter { 36 | override fun attributesOf(type: IElementType, colorScheme: ColorScheme) = when (type) { 37 | JavaTokenType.IDENTIFIER -> colorScheme.identifiers 38 | JavaTokenType.CHARACTER_LITERAL -> colorScheme.charLiteral 39 | JavaTokenType.STRING_LITERAL -> colorScheme.string 40 | JavaTokenType.END_OF_LINE_COMMENT -> colorScheme.lineComments 41 | JavaTokenType.C_STYLE_COMMENT -> colorScheme.blockComments 42 | JavaTokenType.SEMICOLON -> colorScheme.semicolon 43 | JavaTokenType.COLON -> colorScheme.colon 44 | JavaTokenType.COMMA -> colorScheme.comma 45 | JavaDocElementType.DOC_COMMENT -> colorScheme.docComments 46 | JavaTokenType.INTEGER_LITERAL, 47 | JavaTokenType.LONG_LITERAL, 48 | JavaTokenType.DOUBLE_LITERAL, 49 | JavaTokenType.FLOAT_LITERAL -> colorScheme.numbers 50 | JavaTokenType.LPARENTH, JavaTokenType.RPARENTH -> colorScheme.parentheses 51 | JavaTokenType.LBRACE, JavaTokenType.RBRACE -> colorScheme.braces 52 | JavaTokenType.LBRACKET, JavaTokenType.RBRACKET -> colorScheme.brackets 53 | in JAVA_OPERATORS -> colorScheme.operators 54 | in JAVA_KEYWORDS -> colorScheme.keywords 55 | else -> null 56 | } 57 | 58 | private companion object TokenSets { 59 | private val JAVA_OPERATORS = TokenSet.create( 60 | JavaTokenType.DOT, 61 | JavaTokenType.ASTERISK 62 | ) 63 | private val JAVA_KEYWORDS = TokenSet.create( 64 | JavaTokenType.TRUE_KEYWORD, 65 | JavaTokenType.FALSE_KEYWORD, 66 | JavaTokenType.NULL_KEYWORD, 67 | JavaTokenType.ABSTRACT_KEYWORD, 68 | JavaTokenType.ASSERT_KEYWORD, 69 | JavaTokenType.BOOLEAN_KEYWORD, 70 | JavaTokenType.BREAK_KEYWORD, 71 | JavaTokenType.BYTE_KEYWORD, 72 | JavaTokenType.CASE_KEYWORD, 73 | JavaTokenType.CATCH_KEYWORD, 74 | JavaTokenType.CHAR_KEYWORD, 75 | JavaTokenType.CLASS_KEYWORD, 76 | JavaTokenType.CONST_KEYWORD, 77 | JavaTokenType.CONTINUE_KEYWORD, 78 | JavaTokenType.DEFAULT_KEYWORD, 79 | JavaTokenType.DO_KEYWORD, 80 | JavaTokenType.DOUBLE_KEYWORD, 81 | JavaTokenType.ELSE_KEYWORD, 82 | JavaTokenType.ENUM_KEYWORD, 83 | JavaTokenType.EXTENDS_KEYWORD, 84 | JavaTokenType.FINAL_KEYWORD, 85 | JavaTokenType.FINALLY_KEYWORD, 86 | JavaTokenType.FLOAT_KEYWORD, 87 | JavaTokenType.FOR_KEYWORD, 88 | JavaTokenType.GOTO_KEYWORD, 89 | JavaTokenType.IF_KEYWORD, 90 | JavaTokenType.IMPLEMENTS_KEYWORD, 91 | JavaTokenType.IMPORT_KEYWORD, 92 | JavaTokenType.INSTANCEOF_KEYWORD, 93 | JavaTokenType.INT_KEYWORD, 94 | JavaTokenType.INTERFACE_KEYWORD, 95 | JavaTokenType.LONG_KEYWORD, 96 | JavaTokenType.NATIVE_KEYWORD, 97 | JavaTokenType.NEW_KEYWORD, 98 | JavaTokenType.PACKAGE_KEYWORD, 99 | JavaTokenType.PRIVATE_KEYWORD, 100 | JavaTokenType.PUBLIC_KEYWORD, 101 | JavaTokenType.SHORT_KEYWORD, 102 | JavaTokenType.SUPER_KEYWORD, 103 | JavaTokenType.SWITCH_KEYWORD, 104 | JavaTokenType.SYNCHRONIZED_KEYWORD, 105 | JavaTokenType.THIS_KEYWORD, 106 | JavaTokenType.THROW_KEYWORD, 107 | JavaTokenType.PROTECTED_KEYWORD, 108 | JavaTokenType.TRANSIENT_KEYWORD, 109 | JavaTokenType.RETURN_KEYWORD, 110 | JavaTokenType.VOID_KEYWORD, 111 | JavaTokenType.STATIC_KEYWORD, 112 | JavaTokenType.STRICTFP_KEYWORD, 113 | JavaTokenType.WHILE_KEYWORD, 114 | JavaTokenType.TRY_KEYWORD, 115 | JavaTokenType.VOLATILE_KEYWORD, 116 | JavaTokenType.THROWS_KEYWORD 117 | ) 118 | } 119 | } 120 | 121 | /** 122 | * @author ice1000 123 | * @since v1.3 124 | */ 125 | class PlainTextSyntaxHighlighter : SyntaxHighlighter { 126 | override fun attributesOf(type: IElementType, colorScheme: ColorScheme): TextAttributes? = null 127 | } 128 | -------------------------------------------------------------------------------- /common/src/org/jetbrains/kotlin/com/intellij/lexer/LayeredLexer.java: -------------------------------------------------------------------------------- 1 | package org.jetbrains.kotlin.com.intellij.lexer; 2 | 3 | import org.jetbrains.annotations.Contract; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType; 7 | 8 | import java.util.HashMap; 9 | import java.util.HashSet; 10 | import java.util.Map; 11 | 12 | @SuppressWarnings("WeakerAccess") 13 | public class LayeredLexer extends DelegateLexer { 14 | public static ThreadLocal ourDisableLayersFlag = new ThreadLocal<>(); 15 | private int myState; 16 | private final Map myStartTokenToLayerLexer = new HashMap<>(); 17 | private Lexer myCurrentLayerLexer; 18 | private IElementType myCurrentBaseTokenType; 19 | private int myLayerLeftPart = -1; 20 | private int myBaseTokenEnd = -1; 21 | private final HashSet mySelfStoppingLexers = new HashSet<>(1); 22 | private final HashMap myStopTokens = new HashMap<>(1); 23 | 24 | public LayeredLexer(Lexer baseLexer) { 25 | super(baseLexer); 26 | } 27 | 28 | public void registerSelfStoppingLayer(Lexer lexer, IElementType[] startTokens, IElementType[] stopTokens) { 29 | if (!Boolean.TRUE.equals(ourDisableLayersFlag.get())) { 30 | this.registerLayer(lexer, startTokens); 31 | this.mySelfStoppingLexers.add(lexer); 32 | this.myStopTokens.put(lexer, stopTokens); 33 | } 34 | } 35 | 36 | public void registerLayer(Lexer lexer, IElementType... startTokens) { 37 | if (!Boolean.TRUE.equals(ourDisableLayersFlag.get())) { 38 | for (IElementType startToken : startTokens) this.myStartTokenToLayerLexer.put(startToken, lexer); 39 | } 40 | } 41 | 42 | private void activateLayerIfNecessary() { 43 | IElementType baseTokenType = super.getTokenType(); 44 | this.myCurrentLayerLexer = this.findLayerLexer(baseTokenType); 45 | if (this.myCurrentLayerLexer != null) { 46 | this.myCurrentBaseTokenType = baseTokenType; 47 | this.myBaseTokenEnd = super.getTokenEnd(); 48 | this.myCurrentLayerLexer.start(super.getBufferSequence(), super.getTokenStart(), super.getTokenEnd()); 49 | if (this.mySelfStoppingLexers.contains(this.myCurrentLayerLexer)) { 50 | super.advance(); 51 | } 52 | } 53 | 54 | } 55 | 56 | @Nullable 57 | protected Lexer findLayerLexer(IElementType baseTokenType) { 58 | return this.myStartTokenToLayerLexer.get(baseTokenType); 59 | } 60 | 61 | public void start(@NotNull CharSequence buffer, int startOffset, int endOffset, int initialState) { 62 | this.myState = initialState; 63 | this.myCurrentLayerLexer = null; 64 | super.start(buffer, startOffset, endOffset, initialState); 65 | this.activateLayerIfNecessary(); 66 | } 67 | 68 | public int getState() { 69 | return this.myState; 70 | } 71 | 72 | public IElementType getTokenType() { 73 | if (this.isInLayerEndGap()) { 74 | return this.myCurrentBaseTokenType; 75 | } else { 76 | return this.isLayerActive() ? this.myCurrentLayerLexer.getTokenType() : super.getTokenType(); 77 | } 78 | } 79 | 80 | public int getTokenStart() { 81 | if (this.isInLayerEndGap()) { 82 | return this.myLayerLeftPart; 83 | } else { 84 | return this.isLayerActive() ? this.myCurrentLayerLexer.getTokenStart() : super.getTokenStart(); 85 | } 86 | } 87 | 88 | public int getTokenEnd() { 89 | if (this.isInLayerEndGap()) { 90 | return this.myBaseTokenEnd; 91 | } else { 92 | return this.isLayerActive() ? this.myCurrentLayerLexer.getTokenEnd() : super.getTokenEnd(); 93 | } 94 | } 95 | 96 | public void advance() { 97 | if (this.isInLayerEndGap()) { 98 | this.myLayerLeftPart = -1; 99 | this.myState = super.getState(); 100 | } else { 101 | if (this.isLayerActive()) { 102 | Lexer activeLayerLexer = this.myCurrentLayerLexer; 103 | IElementType layerTokenType = activeLayerLexer.getTokenType(); 104 | if (!this.isStopToken(this.myCurrentLayerLexer, layerTokenType)) { 105 | this.myCurrentLayerLexer.advance(); 106 | layerTokenType = this.myCurrentLayerLexer.getTokenType(); 107 | } else { 108 | layerTokenType = null; 109 | } 110 | 111 | if (layerTokenType == null) { 112 | int tokenEnd = this.myCurrentLayerLexer.getTokenEnd(); 113 | if (!this.mySelfStoppingLexers.contains(this.myCurrentLayerLexer)) { 114 | this.myCurrentLayerLexer = null; 115 | super.advance(); 116 | this.activateLayerIfNecessary(); 117 | } else { 118 | this.myCurrentLayerLexer = null; 119 | if (tokenEnd != this.myBaseTokenEnd) { 120 | this.myState = 2048; 121 | this.myLayerLeftPart = tokenEnd; 122 | return; 123 | } 124 | } 125 | } 126 | } else { 127 | super.advance(); 128 | this.activateLayerIfNecessary(); 129 | } 130 | 131 | this.myState = this.isLayerActive() ? 1024 : super.getState(); 132 | } 133 | } 134 | 135 | @NotNull 136 | public LexerPosition getCurrentPosition() { 137 | return new LexerPositionImpl(this.getTokenStart(), this.getState()); 138 | } 139 | 140 | public void restore(@NotNull LexerPosition position) { 141 | this.start(this.getBufferSequence(), position.getOffset(), this.getBufferEnd(), position.getState()); 142 | } 143 | 144 | private boolean isStopToken(Lexer lexer, IElementType tokenType) { 145 | IElementType[] stopTokens = this.myStopTokens.get(lexer); 146 | if (stopTokens == null) { 147 | return false; 148 | } else { 149 | for (IElementType stopToken : stopTokens) { 150 | if (stopToken == tokenType) { 151 | return true; 152 | } 153 | } 154 | 155 | return false; 156 | } 157 | } 158 | 159 | protected boolean isLayerActive() { 160 | return this.myCurrentLayerLexer != null; 161 | } 162 | 163 | @Contract(pure = true) 164 | private boolean isInLayerEndGap() { 165 | return this.myLayerLeftPart != -1; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /common/src/org/ice1000/devkt/api-wrapper.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Copied from the tests in Analyzer compiler 3 | * @author ice1000 4 | */ 5 | package org.ice1000.devkt 6 | 7 | import org.ice1000.devkt.openapi.util.selfLocationFile 8 | import org.jetbrains.kotlin.analyzer.AnalysisResult 9 | import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig 10 | import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory 11 | import org.jetbrains.kotlin.backend.jvm.jvmPhases 12 | import org.jetbrains.kotlin.cli.common.output.writeAllTo 13 | import org.jetbrains.kotlin.cli.common.repl.KotlinJsr223JvmScriptEngineFactoryBase 14 | import org.jetbrains.kotlin.cli.common.repl.ScriptArgsWithTypes 15 | import org.jetbrains.kotlin.cli.jvm.compiler.CliBindingTrace 16 | import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment 17 | import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM 18 | import org.jetbrains.kotlin.codegen.* 19 | import org.jetbrains.kotlin.codegen.state.GenerationState 20 | import org.jetbrains.kotlin.com.intellij.openapi.project.Project 21 | import org.jetbrains.kotlin.com.intellij.psi.search.GlobalSearchScope 22 | import org.jetbrains.kotlin.config.CompilerConfiguration 23 | import org.jetbrains.kotlin.config.JVMConfigurationKeys 24 | import org.jetbrains.kotlin.load.kotlin.PackagePartProvider 25 | import org.jetbrains.kotlin.psi.KtFile 26 | import org.jetbrains.kotlin.resolve.AnalyzingUtils 27 | import org.jetbrains.kotlin.resolve.BindingTrace 28 | import org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngine 29 | import org.jetbrains.kotlin.script.jsr223.KotlinStandardJsr223ScriptTemplate 30 | import java.io.File 31 | import javax.script.Bindings 32 | import javax.script.ScriptContext 33 | import javax.script.ScriptEngine 34 | 35 | fun analyzeAndCheckForErrors(file: KtFile, environment: KotlinCoreEnvironment): AnalysisResult = 36 | analyzeAndCheckForErrors(setOf(file), environment) 37 | 38 | fun analyzeAndCheckForErrors(files: Collection, environment: KotlinCoreEnvironment): AnalysisResult = 39 | analyzeAndCheckForErrors(environment.project, files, environment.configuration, environment::createPackagePartProvider) 40 | 41 | fun analyzeAndCheckForErrors( 42 | project: Project, 43 | files: Collection, 44 | configuration: CompilerConfiguration, 45 | packagePartProvider: (GlobalSearchScope) -> PackagePartProvider, 46 | trace: BindingTrace = CliBindingTrace() 47 | ): AnalysisResult { 48 | for (file in files) AnalyzingUtils.checkForSyntacticErrors(file) 49 | 50 | return analyze(project, files, configuration, packagePartProvider, trace).apply { 51 | AnalyzingUtils.throwExceptionOnErrors(bindingContext) 52 | } 53 | } 54 | 55 | fun analyze(file: KtFile, environment: KotlinCoreEnvironment): AnalysisResult = 56 | analyze(setOf(file), environment) 57 | 58 | fun analyze(files: Collection, environment: KotlinCoreEnvironment): AnalysisResult = 59 | analyze(files, environment, environment.configuration) 60 | 61 | fun analyze( 62 | files: Collection, 63 | environment: KotlinCoreEnvironment, 64 | configuration: CompilerConfiguration): AnalysisResult = 65 | analyze(environment.project, files, configuration, environment::createPackagePartProvider) 66 | 67 | private fun analyze( 68 | project: Project, 69 | files: Collection, 70 | configuration: CompilerConfiguration, 71 | packagePartProviderFactory: (GlobalSearchScope) -> PackagePartProvider, 72 | trace: BindingTrace = CliBindingTrace() 73 | ): AnalysisResult = TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration( 74 | project, files, trace, configuration, packagePartProviderFactory 75 | ) 76 | 77 | fun compileFileTo(ktFile: KtFile, environment: KotlinCoreEnvironment, output: File): ClassFileFactory = 78 | compileFile(ktFile, environment).apply { writeAllTo(output) } 79 | 80 | fun compileFile(ktFile: KtFile, environment: KotlinCoreEnvironment): ClassFileFactory = 81 | compileFiles(listOf(ktFile), environment).factory 82 | 83 | fun compileFiles( 84 | files: List, 85 | environment: KotlinCoreEnvironment, 86 | classBuilderFactory: ClassBuilderFactory = ClassBuilderFactories.TEST, 87 | trace: BindingTrace = CliBindingTrace() 88 | ): GenerationState = 89 | compileFiles(files, environment.configuration, classBuilderFactory, environment::createPackagePartProvider, trace) 90 | 91 | fun compileFiles( 92 | files: List, 93 | configuration: CompilerConfiguration, 94 | classBuilderFactory: ClassBuilderFactory, 95 | packagePartProvider: (GlobalSearchScope) -> PackagePartProvider, 96 | trace: BindingTrace = CliBindingTrace() 97 | ): GenerationState { 98 | val analysisResult = 99 | analyzeAndCheckForErrors(files.first().project, files, configuration, packagePartProvider, trace) 100 | analysisResult.throwIfError() 101 | 102 | val state = GenerationState.Builder( 103 | files.first().project, classBuilderFactory, analysisResult.moduleDescriptor, analysisResult.bindingContext, 104 | files, configuration 105 | ).codegenFactory( 106 | if (configuration.getBoolean(JVMConfigurationKeys.IR)) 107 | JvmIrCodegenFactory(PhaseConfig(jvmPhases)) 108 | else DefaultCodegenFactory 109 | ).build() 110 | if (analysisResult.shouldGenerateCode) 111 | KotlinCodegenFacade.compileCorrectFiles(state) 112 | 113 | // For JVM-specific errors 114 | AnalyzingUtils.throwExceptionOnErrors(state.collectedExtraJvmDiagnostics) 115 | return state 116 | } 117 | 118 | object DevKtScriptEngineFactory : KotlinJsr223JvmScriptEngineFactoryBase() { 119 | override fun getScriptEngine(): ScriptEngine = KotlinJsr223JvmLocalScriptEngine( 120 | this, 121 | listOf(selfLocationFile), 122 | KotlinStandardJsr223ScriptTemplate::class.java.canonicalName, 123 | { ctx, types -> 124 | ScriptArgsWithTypes(arrayOf(ctx.getBindings(ScriptContext.ENGINE_SCOPE)), types ?: emptyArray()) 125 | }, 126 | arrayOf(Bindings::class) 127 | ) 128 | } 129 | 130 | fun compileScript(file: KtFile, configuration: CompilerConfiguration) { 131 | 132 | } 133 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /swing/src/ui/swing/menu.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.ui.swing 2 | 3 | import charlie.gensokyo.* 4 | import org.ice1000.devkt.config.GlobalSettings 5 | import org.ice1000.devkt.ui.DevKtIcons 6 | import org.jetbrains.kotlin.com.intellij.openapi.util.SystemInfo 7 | import java.awt.event.KeyEvent 8 | import javax.swing.JMenuBar 9 | 10 | /** 11 | * DSL that initializes [menuBar] 12 | * 13 | * @author ice1000 14 | * @since v0.0.1 15 | */ 16 | fun UIImpl.mainMenu(menuBar: JMenuBar) { 17 | menuBar.subMenu("File") { 18 | mnemonic = KeyEvent.VK_F 19 | subMenu("New") { 20 | item("Executable File") { 21 | icon = DevKtIcons.KOTLIN_FILE 22 | onAction { createNewFile("file.kt") } 23 | } 24 | item("Script") { 25 | icon = DevKtIcons.KOTLIN_FILE 26 | onAction { createNewFile("script.kts") } 27 | } 28 | item("Android Activity") { 29 | icon = DevKtIcons.KOTLIN_ANDROID 30 | onAction { createNewFile("activity.kt") } 31 | } 32 | item("Analyzer Gradle File") { 33 | icon = DevKtIcons.GRADLE 34 | onAction { createNewFile("build.gradle.kts") } 35 | } 36 | item("KotlinJS File") { 37 | icon = DevKtIcons.KOTLIN_JS 38 | onAction { createNewFile("js.kt") } 39 | } 40 | item("Multiplatform (Common)") { 41 | icon = DevKtIcons.KOTLIN_MP 42 | onAction { createNewFile("mp-common.kt") } 43 | } 44 | item("Multiplatform (Implementation)") { 45 | icon = DevKtIcons.KOTLIN_MP 46 | onAction { createNewFile("mp-impl.kt") } 47 | } 48 | } 49 | item("Open...") { 50 | icon = DevKtIcons.OPEN 51 | onAction { open() } 52 | GlobalSettings.shortcutGoto.isAlt 53 | keyMap(GlobalSettings.shortcutOpen) 54 | } 55 | showInFilesMenuItem = item("Show in Files") { 56 | onAction { showInFiles() } 57 | } 58 | subMenu("Open Recent") { 59 | GlobalSettings.recentFiles.forEach { recent -> 60 | val presentableFile = currentFile?.let { current -> recent.relativeTo(current.parentFile) } 61 | ?: recent.absoluteFile 62 | item(presentableFile.toString()) { 63 | onAction { loadFile(presentableFile) } 64 | } 65 | } 66 | } 67 | separator 68 | if (!SystemInfo.isMac) item("Settings...") { 69 | icon = DevKtIcons.SETTINGS 70 | onAction { settings() } 71 | } 72 | item("Import Settings...") { 73 | onAction { importSettings() } 74 | } 75 | item("Sync Settings") { 76 | icon = DevKtIcons.REFRESH 77 | onAction { restart() } 78 | } 79 | separator 80 | saveMenuItem = item("Save") { 81 | icon = DevKtIcons.SAVE 82 | onAction { save() } 83 | keyMap(GlobalSettings.shortcutSave) 84 | } 85 | item("Sync") { 86 | icon = DevKtIcons.SYNCHRONIZE 87 | onAction { sync() } 88 | keyMap(GlobalSettings.shortcutSync) 89 | } 90 | separator 91 | if (!SystemInfo.isMac) item("Exit") { 92 | icon = DevKtIcons.EXIT 93 | onAction { exit() } 94 | } 95 | } 96 | menuBar.subMenu("Edit") { 97 | mnemonic = KeyEvent.VK_E 98 | undoMenuItem = item("Undo") { 99 | icon = DevKtIcons.UNDO 100 | onAction { undo() } 101 | keyMap(GlobalSettings.shortcutUndo) 102 | } 103 | redoMenuItem = item("Redo") { 104 | icon = DevKtIcons.REDO 105 | onAction { redo() } 106 | keyMap(GlobalSettings.shortcutRedo) 107 | } 108 | separator 109 | item("Cut") { 110 | icon = DevKtIcons.CUT 111 | onAction { cut() } 112 | } 113 | item("Copy") { 114 | icon = DevKtIcons.COPY 115 | onAction { copy() } 116 | } 117 | item("Paste") { 118 | icon = DevKtIcons.PASTE 119 | onAction { paste() } 120 | } 121 | item("Select All") { 122 | icon = DevKtIcons.SELECT_ALL 123 | onAction { selectAll() } 124 | } 125 | separator 126 | subMenu("Lines") { 127 | item("New Line") { 128 | keyMap(GlobalSettings.shortcutNextLine) 129 | onAction { nextLine() } 130 | } 131 | item("New Line Before") { 132 | keyMap(GlobalSettings.shortcutNewLineBefore) 133 | onAction { newLineBeforeCurrent() } 134 | } 135 | item("Split Line") { 136 | keyMap(GlobalSettings.shortcutSplitLine) 137 | onAction { splitLine() } 138 | } 139 | } 140 | item("Line/Column") { 141 | keyMap(GlobalSettings.shortcutGoto) 142 | onAction { gotoLine() } 143 | } 144 | separator 145 | item("Line Comment") { 146 | keyMap(GlobalSettings.shortcutComment) 147 | onAction { commentCurrent() } 148 | } 149 | item("Block Comment") { 150 | keyMap(GlobalSettings.shortcutBlockComment) 151 | onAction { blockComment() } 152 | } 153 | item("Completion") { 154 | keyMap(GlobalSettings.shortcutCompletion) 155 | onAction { document.showCompletion() } 156 | } 157 | separator 158 | subMenu("Find") { 159 | item("Find") { 160 | icon = DevKtIcons.FIND 161 | keyMap(GlobalSettings.shortcutFind) 162 | onAction { find() } 163 | } 164 | item("Replace") { 165 | icon = DevKtIcons.REPLACE 166 | keyMap(GlobalSettings.shortcutReplace) 167 | onAction { replace() } 168 | } 169 | } 170 | } 171 | buildMenuBar = menuBar.subMenu("Build") { 172 | mnemonic = KeyEvent.VK_B 173 | subMenu("Build As") { 174 | icon = DevKtIcons.COMPILE 175 | item("Jar") { 176 | icon = DevKtIcons.JAR 177 | onAction { buildAsJar() } 178 | } 179 | item("Classes") { 180 | icon = DevKtIcons.CLASS 181 | onAction { buildAsClasses() } 182 | } 183 | item("JavaScript Module") { 184 | icon = DevKtIcons.KOTLIN_JS 185 | onAction { buildAsJs() } 186 | } 187 | } 188 | subMenu("Build and Run As") { 189 | icon = DevKtIcons.EXECUTE 190 | item("Jar") { 191 | icon = DevKtIcons.JAR 192 | onAction { buildJarAndRun() } 193 | } 194 | item("Classes") { 195 | icon = DevKtIcons.CLASS 196 | keyMap(GlobalSettings.shortcutBuildRunAsClass) 197 | onAction { buildClassAndRun() } 198 | } 199 | } 200 | item("Run As KtScript") { 201 | icon = DevKtIcons.KOTLIN_FILE 202 | keyMap(GlobalSettings.shortcutRunAsScript) 203 | onAction { runScript() } 204 | } 205 | } 206 | pluginMenuBar = menuBar.subMenu("Plugins") { 207 | subMenu("Switch Language") { 208 | document.languages.forEach { 209 | item(it.displayName) { 210 | icon = it.icon 211 | onAction { _ -> document.switchLanguage(it) } 212 | } 213 | } 214 | } 215 | } 216 | menuBar.subMenu("Help") { 217 | mnemonic = KeyEvent.VK_H 218 | item("View Psi...") { 219 | icon = DevKtIcons.DUMP 220 | onAction { viewPsi() } 221 | } 222 | item("Source Code") { onAction { viewSource() } } 223 | item("Create Issue") { onAction { createIssue() } } 224 | subMenu("Alternatives") { 225 | item("IntelliJ IDEA") { 226 | icon = DevKtIcons.IDEA 227 | onAction { idea() } 228 | } 229 | item("CLion") { 230 | icon = DevKtIcons.CLION 231 | onAction { clion() } 232 | } 233 | item("Eclipse") { 234 | icon = DevKtIcons.ECLIPSE 235 | onAction { eclipse() } 236 | } 237 | item("Emacs") { 238 | icon = DevKtIcons.EMACS 239 | onAction { emacs() } 240 | } 241 | } 242 | } 243 | } 244 | 245 | -------------------------------------------------------------------------------- /common/src/org/ice1000/devkt/analyze.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt 2 | 3 | import org.ice1000.devkt.config.GlobalSettings 4 | import org.ice1000.devkt.lang.DevKtLanguage 5 | import org.ice1000.devkt.openapi.ExtendedDevKtLanguage 6 | import org.ice1000.devkt.openapi.nodeType 7 | import org.ice1000.devkt.openapi.util.selfLocationFile 8 | import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys 9 | import org.jetbrains.kotlin.cli.common.messages.MessageCollector 10 | import org.jetbrains.kotlin.cli.jvm.compiler.CompileEnvironmentUtil 11 | import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles 12 | import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment 13 | import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot 14 | import org.jetbrains.kotlin.com.intellij.lang.Language 15 | import org.jetbrains.kotlin.com.intellij.lang.LanguageExtension 16 | import org.jetbrains.kotlin.com.intellij.lang.LanguageParserDefinitions 17 | import org.jetbrains.kotlin.com.intellij.lang.ParserDefinition 18 | import org.jetbrains.kotlin.com.intellij.lang.java.JavaLanguage 19 | import org.jetbrains.kotlin.com.intellij.lexer.Lexer 20 | import org.jetbrains.kotlin.com.intellij.openapi.Disposable 21 | import org.jetbrains.kotlin.com.intellij.openapi.project.Project 22 | import org.jetbrains.kotlin.com.intellij.openapi.util.Disposer 23 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 24 | import org.jetbrains.kotlin.com.intellij.psi.PsiFileFactory 25 | import org.jetbrains.kotlin.com.intellij.psi.PsiJavaFile 26 | import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType 27 | import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet 28 | import org.jetbrains.kotlin.config.CompilerConfiguration 29 | import org.jetbrains.kotlin.config.JVMConfigurationKeys 30 | import org.jetbrains.kotlin.idea.KotlinLanguage 31 | import org.jetbrains.kotlin.js.config.JSConfigurationKeys 32 | import org.jetbrains.kotlin.lexer.KtTokens 33 | import org.jetbrains.kotlin.name.FqName 34 | import org.jetbrains.kotlin.psi.KtFile 35 | import org.jetbrains.kotlin.psi.psiUtil.endOffset 36 | import org.jetbrains.kotlin.psi.psiUtil.startOffset 37 | import java.io.File 38 | 39 | data class ASTToken( 40 | val start: Int, 41 | val end: Int, 42 | val text: CharSequence, 43 | val type: IElementType 44 | ) { 45 | constructor(node: PsiElement) : this(node.startOffset, node.endOffset, node.text, node.nodeType) 46 | 47 | val textLength get() = text.length 48 | } 49 | 50 | @Suppress("unused") 51 | /** 52 | * @author ice1000 53 | * @since v0.0.1 54 | */ 55 | object Analyzer : Disposable { 56 | val targetDir = File("./.build-cache") 57 | val targetJar get() = targetDir.resolve(GlobalSettings.jarName) 58 | private val scriptEngine = DevKtScriptEngineFactory.scriptEngine 59 | // private val originalStdout = System.out 60 | // private val originalStderr = System.err 61 | private val jvmEnvironment: KotlinCoreEnvironment 62 | private val jsEnvironment: KotlinCoreEnvironment 63 | private val psiFileFactory: PsiFileFactory 64 | val project: Project 65 | 66 | override fun dispose() = Unit 67 | 68 | init { 69 | val compilerConfiguration = CompilerConfiguration() 70 | compilerConfiguration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE) 71 | compilerConfiguration.put(JVMConfigurationKeys.OUTPUT_JAR, targetJar) 72 | compilerConfiguration.put(JVMConfigurationKeys.OUTPUT_DIRECTORY, targetDir) 73 | // compilerConfiguration.addJvmClasspathRoot(selfLocationFile) 74 | // compilerConfiguration.put(JVMConfigurationKeys.IR, true) 75 | jvmEnvironment = KotlinCoreEnvironment.createForTests(this, 76 | compilerConfiguration, EnvironmentConfigFiles.JVM_CONFIG_FILES) 77 | val jsCompilerConfiguration = CompilerConfiguration() 78 | jsCompilerConfiguration.put(JSConfigurationKeys.OUTPUT_DIR, targetDir) 79 | jsEnvironment = KotlinCoreEnvironment.createForProduction(this, 80 | jsCompilerConfiguration, EnvironmentConfigFiles.JS_CONFIG_FILES) 81 | project = jvmEnvironment.project 82 | psiFileFactory = PsiFileFactory.getInstance(project) 83 | } 84 | 85 | private fun registerExtensionPoint( 86 | extensionPoint: LanguageExtension, 87 | language: Language, 88 | instance: Extension 89 | ) { 90 | extensionPoint.addExplicitExtension(language, instance) 91 | Disposer.register(project, Disposable { 92 | extensionPoint.removeExplicitExtension(language, instance) 93 | }) 94 | } 95 | 96 | // private fun registerExtensionPoint( 97 | // extensionPoint: ExtensionPointName, 98 | // clazz: Class) = registerExtensionPoint(extensionPoint, clazz.newInstance()) 99 | 100 | fun registerLanguage( 101 | language: Language, parserDefinition: ParserDefinition 102 | ) { 103 | LanguageParserDefinitions.INSTANCE.addExplicitExtension(language, parserDefinition) 104 | Disposer.register(project, Disposable { 105 | LanguageParserDefinitions.INSTANCE.removeExplicitExtension(language, parserDefinition) 106 | }) 107 | } 108 | 109 | fun registerLanguage( 110 | extendedProgrammingLanguage: ExtendedDevKtLanguage<*>) = registerLanguage( 111 | extendedProgrammingLanguage.language, 112 | extendedProgrammingLanguage.parserDefinition) 113 | 114 | fun parseKotlin(text: String) = parse(text, KotlinLanguage.INSTANCE) as KtFile 115 | fun parseJava(text: String) = parse(text, JavaLanguage.INSTANCE) as PsiJavaFile 116 | fun parse(text: String, language: DevKtLanguage<*>, name: String? = null) = parse(text, language.language, name) 117 | fun parse(text: String, language: Language, name: String? = null) = psiFileFactory 118 | .createFileFromText(name ?: GlobalSettings.javaClassName, language, text, false, false, true) 119 | 120 | fun compileJvm(ktFile: KtFile) { 121 | ensureTargetDirExists() 122 | compileFileTo(ktFile, jvmEnvironment, targetDir) 123 | } 124 | 125 | fun runScript(text: String) { 126 | scriptEngine.eval(text) 127 | } 128 | 129 | fun compileJar(ktFile: KtFile) { 130 | ensureTargetDirExists() 131 | CompileEnvironmentUtil.writeToJar( 132 | targetJar, 133 | false, 134 | FqName.fromSegments(listOf("devkt", "${GlobalSettings.javaClassName}Kt")), 135 | compileFile(ktFile, jvmEnvironment)) 136 | } 137 | 138 | fun compileJs(ktFile: KtFile) { 139 | ensureTargetDirExists() 140 | TODO() 141 | } 142 | 143 | private fun ensureTargetDirExists() { 144 | if (!targetDir.isDirectory) targetDir.mkdirs() 145 | targetDir.listFiles().orEmpty().forEach { it.deleteRecursively() } 146 | } 147 | 148 | // TODO incremental 149 | fun lex(text: CharSequence, lexer: Lexer) = lexer.run { 150 | start(text) 151 | generateSequence { 152 | tokenType 153 | ?.let { ASTToken(tokenStart, tokenEnd, tokenSequence, it) } 154 | ?.also { advance() } 155 | } 156 | } 157 | } 158 | 159 | val stringTokens = TokenSet.create( 160 | KtTokens.OPEN_QUOTE, 161 | KtTokens.CLOSING_QUOTE, 162 | KtTokens.REGULAR_STRING_PART 163 | ) 164 | 165 | val stringTemplateTokens = TokenSet.create( 166 | KtTokens.SHORT_TEMPLATE_ENTRY_START, 167 | KtTokens.LONG_TEMPLATE_ENTRY_START, 168 | KtTokens.LONG_TEMPLATE_ENTRY_END 169 | ) 170 | 171 | -------------------------------------------------------------------------------- /swing/src/ui/swing/forms/Find.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 |
140 | -------------------------------------------------------------------------------- /swing/src/ui/swing/dialogs/find-replace.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.ui.swing.dialogs 2 | 3 | import com.intellij.uiDesigner.core.GridConstraints 4 | import com.intellij.uiDesigner.core.GridLayoutManager 5 | import com.intellij.uiDesigner.core.Spacer 6 | import org.ice1000.devkt.ui.* 7 | import org.ice1000.devkt.ui.swing.AbstractUI 8 | import java.awt.Component 9 | import java.awt.Dimension 10 | import java.awt.Insets 11 | import javax.swing.* 12 | import javax.swing.event.DocumentEvent 13 | import javax.swing.event.DocumentListener 14 | 15 | abstract class FindUI : JDialog() { 16 | protected val mainPanel: JPanel = JPanel() 17 | protected val isMatchCase: JCheckBox = JCheckBox() 18 | protected val moveUp: JButton = JButton() 19 | protected val moveDown: JButton = JButton() 20 | protected val isRegex: JCheckBox = JCheckBox() 21 | protected val findInput: JTextField = JTextField() 22 | protected val replaceInput: JTextField = JTextField() 23 | protected val replace: JButton = JButton() 24 | protected val replaceAll: JButton = JButton() 25 | protected val separator: JSeparator = JSeparator() 26 | protected val findCount: JLabel = JLabel() 27 | 28 | init { 29 | mainPanel.layout = GridLayoutManager(5, 1, Insets(0, 0, 0, 0), -1, -1) 30 | val panel1 = JPanel() 31 | panel1.layout = GridLayoutManager(1, 2, Insets(0, 0, 0, 0), -1, -1) 32 | mainPanel.add(panel1, GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)) 33 | isMatchCase.text = "Match Case" 34 | isMatchCase.setMnemonic('C') 35 | isMatchCase.displayedMnemonicIndex = 6 36 | panel1.add(isMatchCase, GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)) 37 | isRegex.text = "Regex" 38 | isRegex.setMnemonic('G') 39 | isRegex.displayedMnemonicIndex = 2 40 | panel1.add(isRegex, GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)) 41 | mainPanel.add(findInput, GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, Dimension(150, -1), null, 0, false)) 42 | val panel2 = JPanel() 43 | panel2.layout = GridLayoutManager(1, 5, Insets(0, 0, 0, 0), -1, -1) 44 | mainPanel.add(panel2, GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)) 45 | moveUp.icon = DevKtIcons.MOVE_UP 46 | moveUp.text = "" 47 | panel2.add(moveUp, GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)) 48 | moveDown.icon = DevKtIcons.MOVE_DOWN 49 | moveDown.text = "" 50 | panel2.add(moveDown, GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)) 51 | val spacer1 = Spacer() 52 | panel2.add(spacer1, GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)) 53 | findCount.text = "0/0" 54 | panel2.add(findCount, GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)) 55 | val spacer2 = Spacer() 56 | panel2.add(spacer2, GridConstraints(0, 4, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)) 57 | separator.isVisible = false 58 | mainPanel.add(separator, GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)) 59 | val panel3 = JPanel() 60 | panel3.layout = GridLayoutManager(2, 2, Insets(0, 0, 0, 0), -1, -1) 61 | mainPanel.add(panel3, GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)) 62 | replaceInput.isVisible = false 63 | panel3.add(replaceInput, GridConstraints(0, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, Dimension(150, -1), null, 0, false)) 64 | replace.text = "Replace" 65 | replace.setMnemonic('R') 66 | replace.displayedMnemonicIndex = 0 67 | replace.isVisible = false 68 | panel3.add(replace, GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)) 69 | replaceAll.text = "Replace all" 70 | replaceAll.setMnemonic('A') 71 | replaceAll.displayedMnemonicIndex = 8 72 | replaceAll.isVisible = false 73 | panel3.add(replaceAll, GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK or GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)) 74 | } 75 | } 76 | 77 | open class FindDialogImpl( 78 | uiImpl: AbstractUI, 79 | override val document: DevKtDocumentHandler<*>) : FindUI(), IFind { 80 | protected open val bundle get() = FindDataBundle(findInput.text, isMatchCase.isSelected, isRegex.isSelected) 81 | override val searchResult = arrayListOf() 82 | override var currentIndex = 0 83 | 84 | init { 85 | setLocationRelativeTo(uiImpl.mainPanel) 86 | 87 | contentPane = mainPanel 88 | title = "Find" 89 | isModal = true 90 | 91 | pack() 92 | 93 | moveUp.addActionListener { moveUp() } 94 | moveDown.addActionListener { moveDown() } 95 | isMatchCase.addActionListener { search(bundle) } 96 | isRegex.addActionListener { search(bundle) } 97 | findInput.document.addDocumentListener(object : DocumentListener { 98 | override fun changedUpdate(e: DocumentEvent?) = Unit //不懂调用条件。。。 99 | override fun insertUpdate(e: DocumentEvent?) = removeUpdate(e) 100 | override fun removeUpdate(e: DocumentEvent?) = search(bundle) 101 | }) 102 | } 103 | 104 | override fun update() { 105 | findCount.text = "${currentIndex.takeIf { searchResult.size != 0 }?.plus(1) ?: 0}/${searchResult.size}" 106 | } 107 | 108 | final override fun setLocationRelativeTo(c: Component?) = super.setLocationRelativeTo(c) 109 | final override fun pack() = super.pack() 110 | } 111 | 112 | class ReplaceDialogImpl( 113 | uiImpl: AbstractUI, document: DevKtDocumentHandler<*>) : 114 | FindDialogImpl(uiImpl, document), IReplace { 115 | 116 | override val bundle 117 | get() = super.bundle.apply { 118 | replaceInput = this@ReplaceDialogImpl.replaceInput.text 119 | } 120 | 121 | init { 122 | title = "Replace" 123 | listOf(separator, replaceInput, replace, replaceAll).forEach { 124 | it.isVisible = true 125 | } 126 | 127 | pack() 128 | 129 | replace.addActionListener { replaceCurrent(bundle) } 130 | replaceAll.addActionListener { replaceAll(bundle) } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /swing/src/ui/swing/swing-impl.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.ui.swing 2 | 3 | import charlie.gensokyo.show 4 | import com.bennyhuo.kotlin.opd.delegateOf 5 | import net.iharder.dnd.FileDrop 6 | import org.ice1000.devkt.DevKtFontManager.loadFont 7 | import org.ice1000.devkt.config.GlobalSettings 8 | import org.ice1000.devkt.config.swingColorScheme 9 | import org.ice1000.devkt.lang.DevKtLanguage 10 | import org.ice1000.devkt.ui.DevKtDocument 11 | import org.ice1000.devkt.ui.DevKtDocumentHandler 12 | import org.ice1000.devkt.ui.swing.dialogs.FindDialogImpl 13 | import org.ice1000.devkt.ui.swing.dialogs.GoToLineDialog 14 | import org.ice1000.devkt.ui.swing.dialogs.ReplaceDialogImpl 15 | import org.jetbrains.kotlin.psi.KtFile 16 | import java.awt.Font 17 | import java.awt.event.MouseAdapter 18 | import java.awt.event.MouseEvent 19 | import java.io.File 20 | import javax.swing.JMenu 21 | import javax.swing.JMenuItem 22 | import javax.swing.event.DocumentEvent 23 | import javax.swing.text.AttributeSet 24 | import javax.swing.text.DefaultStyledDocument 25 | import javax.swing.text.MutableAttributeSet 26 | 27 | /** 28 | * @author ice1000 29 | * @since v0.0.1 30 | */ 31 | class UIImpl(frame: DevKtFrame) : AbstractUI(frame) { 32 | internal lateinit var undoMenuItem: JMenuItem 33 | internal lateinit var redoMenuItem: JMenuItem 34 | internal lateinit var saveMenuItem: JMenuItem 35 | internal lateinit var showInFilesMenuItem: JMenuItem 36 | internal lateinit var buildMenuBar: JMenu 37 | internal lateinit var pluginMenuBar: JMenu 38 | override val document: DevKtDocumentHandler 39 | 40 | private inner class KtDocument : DefaultStyledDocument(), DevKtDocument { 41 | private val root = defaultRootElement 42 | override var caretPosition by delegateOf(editor::getCaretPosition, editor::setCaretPosition) 43 | override var selectionEnd by delegateOf(editor::getSelectionEnd, editor::setSelectionEnd) 44 | override var selectionStart by delegateOf(editor::getSelectionStart, editor::setSelectionStart) 45 | 46 | fun createHandler() = DevKtDocumentHandler(this, this@UIImpl, swingColorScheme(GlobalSettings, attributeContext)) 47 | override fun startOffsetOf(line: Int) = root.getElement(line).startOffset 48 | override fun endOffsetOf(line: Int) = root.getElement(line).endOffset 49 | override fun lineOf(offset: Int) = root.getElementIndex(offset) 50 | 51 | override fun lockWrite() = writeLock() 52 | override fun unlockWrite() = writeUnlock() 53 | /** from [DevKtDocument] */ 54 | override fun insert(offs: Int, str: String?) = super.insertString(offs, str, null) 55 | 56 | /** from [DevKtDocument] */ 57 | override fun delete(offs: Int, len: Int) = super.remove(offs, len) 58 | 59 | /** from [DefaultStyledDocument] */ 60 | override fun remove(offs: Int, len: Int) = document.delete(offs, len) 61 | 62 | /** from [DefaultStyledDocument] */ 63 | override fun insertString(offs: Int, str: String?, a: AttributeSet?) = document.handleInsert(offs, str) 64 | 65 | override fun resetLineNumberLabel(str: String) { 66 | lineNumberLabel.text = str 67 | } 68 | 69 | override fun onChangeLanguage(newLanguage: DevKtLanguage) { 70 | pluginMenuBar.text = newLanguage.displayName 71 | pluginMenuBar.icon = newLanguage.icon 72 | } 73 | 74 | /** 75 | * Re-implement of [setCharacterAttributes], invoke [fireUndoableEditUpdate] with 76 | * [document] as event source, which is used by [undoManager] to prevent color 77 | * modifications to be recorded. 78 | */ 79 | override fun changeCharacterAttributes(offset: Int, length: Int, s: AttributeSet, replace: Boolean) { 80 | val changes = DefaultDocumentEvent(offset, length, DocumentEvent.EventType.CHANGE) 81 | buffer.change(offset, length, changes) 82 | var lastEnd: Int 83 | var pos = offset 84 | while (pos < offset + length) { 85 | val run = getCharacterElement(pos) 86 | lastEnd = run.endOffset 87 | if (pos == lastEnd) break 88 | val attr = run.attributes as MutableAttributeSet 89 | changes.addEdit(AttributeUndoableEdit(run, s, replace)) 90 | if (replace) attr.removeAttributes(attr) 91 | attr.addAttributes(s) 92 | pos = lastEnd 93 | } 94 | changes.end() 95 | fireChangedUpdate(changes) 96 | } 97 | 98 | /** 99 | * Re-implement of [setParagraphAttributes], invoke [fireUndoableEditUpdate] with 100 | * [GlobalSettings] as event source, which is used by [undoManager] to prevent color 101 | * modifications to be recorded. 102 | */ 103 | override fun changeParagraphAttributes(offset: Int, length: Int, s: AttributeSet, replace: Boolean) = try { 104 | writeLock() 105 | val changes = DefaultDocumentEvent(offset, length, DocumentEvent.EventType.CHANGE) 106 | val sCopy = s.copyAttributes() 107 | for (i in root.getElementIndex(offset)..root.getElementIndex(offset + if (length > 0) length - 1 else 0)) { 108 | val paragraph = root.getElement(i) 109 | val attr = paragraph.attributes as MutableAttributeSet 110 | changes.addEdit(AttributeUndoableEdit(paragraph, sCopy, replace)) 111 | if (replace) attr.removeAttributes(attr) 112 | attr.addAttributes(s) 113 | } 114 | changes.end() 115 | fireChangedUpdate(changes) 116 | } finally { 117 | writeUnlock() 118 | } 119 | } 120 | 121 | init { 122 | val ktDocument = KtDocument() 123 | editor.document = ktDocument 124 | document = ktDocument.createHandler() 125 | editor.addMouseListener(object : MouseAdapter() { 126 | override fun mouseClicked(e: MouseEvent?) { 127 | lastPopup?.hide() 128 | lastPopup = null 129 | } 130 | }) 131 | mainMenu(menuBar) 132 | FileDrop(mainPanel) { 133 | it.firstOrNull { it.canRead() }?.let(::loadFile) 134 | } 135 | } 136 | 137 | /** 138 | * Should only be called once, extracted from the constructor 139 | * to shorten the startup time 140 | */ 141 | @JvmName(" ") 142 | internal fun postInit() { 143 | init() 144 | val lastOpenedFile = File(GlobalSettings.lastOpenedFile) 145 | if (lastOpenedFile.canRead()) { 146 | edited = false 147 | loadFile(lastOpenedFile) 148 | } 149 | } 150 | 151 | fun selectAll() { 152 | message("Select All") 153 | editor.selectAll() 154 | } 155 | 156 | fun cut() { 157 | message("Cut selection") 158 | editor.cut() 159 | } 160 | 161 | fun copy() { 162 | message("Copied selection") 163 | editor.copy() 164 | } 165 | 166 | fun paste() { 167 | message("Pasted to current position") 168 | editor.paste() 169 | } 170 | 171 | fun gotoLine() = GoToLineDialog(this@UIImpl, document.document).show 172 | fun find() = FindDialogImpl(this@UIImpl, document).show 173 | fun replace() = ReplaceDialogImpl(this@UIImpl, document).show 174 | override fun editorText() = editor.text.orEmpty() 175 | 176 | override fun updateShowInFilesMenuItem() { 177 | showInFilesMenuItem.isEnabled = currentFile != null 178 | buildMenuBar.isVisible = document.psiFile is KtFile 179 | // saveMenuItem.isEnabled = currentFileNotNull 180 | } 181 | 182 | override fun updateUndoRedoMenuItem() { 183 | // undoMenuItem.isEnabled = document.canUndo 184 | // redoMenuItem.isEnabled = document.canRedo 185 | } 186 | 187 | /** 188 | * Just to reuse some codes in [reloadSettings] and [postInit] 189 | */ 190 | private fun init() { 191 | with(lineNumberLabel) { 192 | font = editor.font 193 | background = editor.background.brighter() 194 | } 195 | memoryIndicator.font = messageLabel.font.run { deriveFont(size2D - 2.5F) } 196 | } 197 | 198 | override fun reloadSettings() { 199 | frame.bounds = GlobalSettings.windowBounds 200 | imageCache = null 201 | loadFont() 202 | refreshTitle() 203 | init() 204 | with(document) { 205 | adjustFormat() 206 | reparse() 207 | } 208 | } 209 | 210 | override fun refreshTitle() { 211 | frame.title = regenerateTitle() 212 | } 213 | 214 | var editorFont: Font 215 | set(value) { 216 | lineNumberLabel.font = value 217 | editor.font = value 218 | } 219 | get() = editor.font 220 | } 221 | -------------------------------------------------------------------------------- /common/src/org/ice1000/devkt/lang/languages.kt: -------------------------------------------------------------------------------- 1 | package org.ice1000.devkt.lang 2 | 3 | import org.ice1000.devkt.ASTToken 4 | import org.ice1000.devkt.openapi.Annotator 5 | import org.ice1000.devkt.openapi.SyntaxHighlighter 6 | import org.ice1000.devkt.openapi.ui.IDevKtDocumentHandler 7 | import org.ice1000.devkt.openapi.util.CompletionElement 8 | import org.ice1000.devkt.ui.DevKtDocumentHandler 9 | import org.ice1000.devkt.ui.DevKtIcons 10 | import org.jetbrains.kotlin.com.intellij.lang.Language 11 | import org.jetbrains.kotlin.com.intellij.lang.java.JavaLanguage 12 | import org.jetbrains.kotlin.com.intellij.lang.java.lexer.JavaLexer 13 | import org.jetbrains.kotlin.com.intellij.lexer.EmptyLexer 14 | import org.jetbrains.kotlin.com.intellij.lexer.Lexer 15 | import org.jetbrains.kotlin.com.intellij.openapi.fileTypes.PlainTextLanguage 16 | import org.jetbrains.kotlin.com.intellij.openapi.project.Project 17 | import org.jetbrains.kotlin.com.intellij.pom.java.LanguageLevel 18 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace 20 | import org.jetbrains.kotlin.idea.KotlinLanguage 21 | import org.jetbrains.kotlin.lexer.KotlinLexer 22 | import org.jetbrains.kotlin.psi.KtParameter 23 | import javax.swing.Icon 24 | 25 | /** 26 | * @author ice1000 27 | * @since v1.2 28 | * @see Language 29 | */ 30 | interface DevKtLanguage : Annotator, SyntaxHighlighter { 31 | val language: Language 32 | 33 | /** 34 | * Language name displayed on menu bar. 35 | */ 36 | @JvmDefault 37 | val displayName: String 38 | get() = language.displayName 39 | 40 | /** 41 | * @param currentElement PsiElement the current psi element user's typing on 42 | * @param inputString String the string user typed, in most cases it's just one char 43 | * @return Boolean whether to show the completion popup or not 44 | * @see com.intellij.codeInsight.completion.CompletionContributor.invokeAutoPopup 45 | */ 46 | @JvmDefault 47 | fun invokeAutoPopup(currentElement: ASTToken, inputString: String) = 48 | inputString.length == 1 && inputString[0].let { it.isLetter() || it in "@." } 49 | 50 | /** 51 | * @param element PsiElement An element in the current AST 52 | * @return Boolean whether this element should be added into the completion list 53 | */ 54 | @JvmDefault 55 | fun shouldAddAsCompletion(element: PsiElement) = false 56 | 57 | /** 58 | * Check if a file is of this language 59 | * @param fileName String the file name 60 | * @return Boolean is of this language or not 61 | */ 62 | @JvmDefault 63 | fun satisfies(fileName: String) = false 64 | 65 | /** 66 | * Line comment start, used when pressing Ctrl + / 67 | */ 68 | @JvmDefault 69 | val lineCommentStart: String? 70 | get() = null 71 | 72 | /** 73 | * Block comment surrounding, used when pressing 74 | * Ctrl + Shift + / 75 | */ 76 | @JvmDefault 77 | val blockComment: Pair? 78 | get() = null 79 | 80 | @JvmDefault 81 | val icon: Icon 82 | get() = DevKtIcons.ANY 83 | 84 | fun createLexer(project: Project): Lexer 85 | 86 | /** 87 | * Called when typing, before the typed character is inserted. 88 | * 89 | * @param offset Int 90 | * @param text String? 91 | * @param element PsiElement? 92 | * @param document DevKtDocument 93 | * @see org.ice1000.devkt.ui.DevKtDocumentHandler.insertDirectly 94 | * @see org.ice1000.devkt.ui.DevKtDocumentHandler.insert 95 | */ 96 | @JvmDefault 97 | fun handleTyping( 98 | offset: Int, 99 | text: String?, 100 | element: PsiElement?, 101 | document: IDevKtDocumentHandler) { 102 | document.insert(offset, text) 103 | } 104 | 105 | @JvmDefault 106 | val initialCompletionElementList: Set 107 | get() = emptySet() 108 | } 109 | 110 | /** 111 | * @author ice1000 112 | * @since v1.2 113 | * @see DevKtLanguage 114 | */ 115 | sealed class BuiltinDevKtLanguage( 116 | annotator: Annotator, 117 | syntaxHighlighter: SyntaxHighlighter, 118 | override val language: Language 119 | ) : DevKtLanguage, 120 | Annotator by annotator, 121 | SyntaxHighlighter by syntaxHighlighter { 122 | override fun satisfies(fileName: String) = false 123 | override val lineCommentStart = "//" 124 | override val blockComment: Pair? = "/*" to "*/" 125 | override fun handleTyping( 126 | offset: Int, 127 | text: String?, 128 | element: PsiElement?, 129 | document: IDevKtDocumentHandler) = element.takeIf { text == "\n" }?.run { 130 | val whitespace = containingFile.findElementAt(document.startOffsetOf(document.lineOf(offset))) 131 | as? PsiWhiteSpace ?: return@run null 132 | val text = whitespace.text.split('\n').last().let { "\n$it" } 133 | super.handleTyping(offset, text, element, document) 134 | } ?: super.handleTyping(offset, text, element, document) 135 | } 136 | 137 | /** 138 | * @author ice1000 139 | * @since v1.2 140 | * @see JavaLanguage 141 | */ 142 | class Java : BuiltinDevKtLanguage( 143 | JavaAnnotator(), 144 | JavaSyntaxHighlighter(), 145 | JavaLanguage.INSTANCE) { 146 | override fun satisfies(fileName: String) = fileName.endsWith(".java") 147 | private val java8Lexer = JavaLexer(LanguageLevel.JDK_1_9) 148 | // TODO multiple language level support 149 | override fun createLexer(project: Project) = java8Lexer 150 | 151 | override val displayName: String get() = "Java9" 152 | override val icon: Icon get() = DevKtIcons.JAVA 153 | 154 | override val initialCompletionElementList: Set = listOf( 155 | "true", "false", "null", "abstract", "assert", "boolean", "break", 156 | "byte", "case", "catch", "char", "class", "const", "continue", "default", 157 | "do", "double", "else", "enum", "extends", "final", "finally", "float", 158 | "for", "goto", "if", "implements", "import", "instanceof", "int", 159 | "interface", "long", "native", "new", "package", "private", "public", 160 | "short", "super", "switch", "synchronized", "this", "throw", "protected", 161 | "transient", "return", "void", "static", "strictfp", "while", "try", 162 | "volatile", "throws" 163 | ).mapTo(hashSetOf( 164 | object : CompletionElement("System.out.println", lookup = "sout") { 165 | override fun afterInsert(documentHandler: DevKtDocumentHandler<*>) = 166 | documentHandler.insert("(") 167 | }, 168 | object : CompletionElement("System.err.println", lookup = "serr") { 169 | override fun afterInsert(documentHandler: DevKtDocumentHandler<*>) = 170 | documentHandler.insert("(") 171 | }, 172 | object : CompletionElement("public static void main(String... args) ", lookup = "psvm") { 173 | override fun afterInsert(documentHandler: DevKtDocumentHandler<*>) { 174 | documentHandler.insert("{") 175 | documentHandler.insert("\n") 176 | } 177 | } 178 | )) { 179 | CompletionElement(it, type = "Keyword") 180 | } 181 | } 182 | 183 | /** 184 | * @author ice1000 185 | * @since v1.2 186 | * @see KotlinLanguage 187 | */ 188 | class Kotlin : BuiltinDevKtLanguage( 189 | KotlinAnnotator(), 190 | KotlinSyntaxHighlighter(), 191 | KotlinLanguage.INSTANCE) { 192 | override fun satisfies(fileName: String) = fileName.endsWith(".kt") or fileName.endsWith(".kts") 193 | private val lexer = KotlinLexer() 194 | override fun createLexer(project: Project) = lexer 195 | override val icon: Icon get() = DevKtIcons.KOTLIN_FILE 196 | override fun shouldAddAsCompletion(element: PsiElement): Boolean { 197 | return element is KtParameter || super.shouldAddAsCompletion(element) 198 | } 199 | } 200 | 201 | /** 202 | * @author ice1000 203 | * @since v1.2 204 | * @see PlainTextLanguage 205 | */ 206 | class PlainText : DevKtLanguage, 207 | Annotator by PlainTextAnnotator(), 208 | SyntaxHighlighter by PlainTextSyntaxHighlighter() { 209 | override val language: PlainTextLanguage = PlainTextLanguage.INSTANCE 210 | override fun satisfies(fileName: String) = 211 | fileName.endsWith(".txt") && !fileName.endsWith(".lua.txt") || '.' !in fileName 212 | 213 | private val lexer = EmptyLexer() 214 | override fun createLexer(project: Project) = lexer 215 | } 216 | -------------------------------------------------------------------------------- /common/src/org/jetbrains/kotlin/com/intellij/lexer/StringLiteralLexer.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2000-2015 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.jetbrains.kotlin.com.intellij.lexer 17 | 18 | import org.ice1000.devkt.openapi.util.isHex 19 | import org.jetbrains.kotlin.com.intellij.psi.StringEscapesTokenTypes 20 | import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType 21 | 22 | /** 23 | * @author max 24 | */ 25 | @Suppress("MemberVisibilityCanBePrivate") 26 | open class StringLiteralLexer 27 | /** 28 | * @param myCanEscapeEolOrFramingSpaces true if following sequences are acceptable 29 | * '\' in the end of the buffer (meaning escaped end of line) or 30 | */ 31 | @JvmOverloads constructor( 32 | protected val myQuoteChar: Char, 33 | protected val myOriginalLiteralToken: IElementType, 34 | private val myCanEscapeEolOrFramingSpaces: Boolean = false, 35 | private val myAdditionalValidEscapes: String? = null, 36 | private val myAllowOctal: Boolean = true, 37 | private val myAllowHex: Boolean = false) : LexerBase() { 38 | 39 | protected lateinit var myBuffer: CharSequence 40 | protected var myStart: Int = 0 41 | protected var myEnd: Int = 0 42 | private var myState: Int = 0 43 | private var myLastState: Int = 0 44 | protected var myBufferEnd: Int = 0 45 | private var mySeenEscapedSpacesOnly: Boolean = false 46 | 47 | protected val unicodeEscapeSequenceType: IElementType 48 | get() { 49 | for (i in myStart + 2 until myStart + 6) { 50 | if (i >= myEnd || !isHex(myBuffer[i])) 51 | return StringEscapesTokenTypes.INVALID_UNICODE_ESCAPE_TOKEN 52 | } 53 | return StringEscapesTokenTypes.VALID_STRING_ESCAPE_TOKEN 54 | } 55 | 56 | override fun start(buffer: CharSequence, startOffset: Int, endOffset: Int, initialState: Int) { 57 | myBuffer = buffer 58 | myStart = startOffset 59 | myState = if (myQuoteChar == NO_QUOTE_CHAR) AFTER_FIRST_QUOTE.toInt() else initialState 60 | myLastState = initialState 61 | myBufferEnd = endOffset 62 | myEnd = locateToken(myStart) 63 | mySeenEscapedSpacesOnly = true 64 | } 65 | 66 | override fun getState(): Int { 67 | return myLastState 68 | } 69 | 70 | override fun getTokenType(): IElementType? { 71 | if (myStart >= myEnd) return null 72 | 73 | if (myBuffer[myStart] != '\\') { 74 | mySeenEscapedSpacesOnly = false 75 | return myOriginalLiteralToken 76 | } 77 | 78 | if (myStart + 1 >= myEnd) { 79 | return handleSingleSlashEscapeSequence() 80 | } 81 | val nextChar = myBuffer[myStart + 1] 82 | mySeenEscapedSpacesOnly = mySeenEscapedSpacesOnly and (nextChar == ' ') 83 | if (myCanEscapeEolOrFramingSpaces && (nextChar == '\n' || nextChar == ' ' && (mySeenEscapedSpacesOnly || isTrailingSpace( 84 | myStart + 2)))) { 85 | return StringEscapesTokenTypes.VALID_STRING_ESCAPE_TOKEN 86 | } 87 | if (nextChar == 'u') { 88 | return unicodeEscapeSequenceType 89 | } 90 | 91 | if (nextChar == 'x' && myAllowHex) { 92 | for (i in myStart + 2 until myStart + 4) { 93 | if (i >= myEnd || !isHex(myBuffer[i])) return StringEscapesTokenTypes.INVALID_UNICODE_ESCAPE_TOKEN 94 | } 95 | return StringEscapesTokenTypes.VALID_STRING_ESCAPE_TOKEN 96 | } 97 | 98 | when (nextChar) { 99 | '0', '1', '2', '3', '4', '5', '6', '7' -> { 100 | return if (!myAllowOctal) StringEscapesTokenTypes.INVALID_CHARACTER_ESCAPE_TOKEN else StringEscapesTokenTypes.VALID_STRING_ESCAPE_TOKEN 101 | } 102 | 103 | 'n', 'r', 'b', 't', 'f', '\'', '\"', '\\' -> return StringEscapesTokenTypes.VALID_STRING_ESCAPE_TOKEN 104 | } 105 | return if (myAdditionalValidEscapes != null && myAdditionalValidEscapes.indexOf(nextChar) != -1) { 106 | StringEscapesTokenTypes.VALID_STRING_ESCAPE_TOKEN 107 | } else StringEscapesTokenTypes.INVALID_CHARACTER_ESCAPE_TOKEN 108 | 109 | } 110 | 111 | protected fun handleSingleSlashEscapeSequence(): IElementType { 112 | return StringEscapesTokenTypes.INVALID_CHARACTER_ESCAPE_TOKEN 113 | } 114 | 115 | // all subsequent chars are escaped spaces 116 | private fun isTrailingSpace(start: Int): Boolean { 117 | var i = start 118 | while (i < myBufferEnd) { 119 | val c = myBuffer[i] 120 | if (c != '\\') return false 121 | if (i == myBufferEnd - 1) return false 122 | if (myBuffer[i + 1] != ' ') return false 123 | i += 2 124 | } 125 | return true 126 | } 127 | 128 | override fun getTokenStart() = myStart 129 | override fun getTokenEnd() = myEnd 130 | 131 | private fun locateToken(start: Int): Int { 132 | if (start == myBufferEnd) { 133 | myState = AFTER_LAST_QUOTE.toInt() 134 | } 135 | if (myState == AFTER_LAST_QUOTE.toInt()) return start 136 | var i = start 137 | if (myBuffer[i] == '\\') { 138 | i++ 139 | if (i == myBufferEnd || myBuffer[i] == '\n' && !myCanEscapeEolOrFramingSpaces) { 140 | myState = AFTER_LAST_QUOTE.toInt() 141 | return i 142 | } 143 | 144 | if (myAllowOctal && myBuffer[i] >= '0' && myBuffer[i] <= '7') { 145 | val first = myBuffer[i] 146 | i++ 147 | if (i < myBufferEnd && myBuffer[i] >= '0' && myBuffer[i] <= '7') { 148 | i++ 149 | if (i < myBufferEnd && first <= '3' && myBuffer[i] >= '0' && myBuffer[i] <= '7') { 150 | i++ 151 | } 152 | } 153 | return i 154 | } 155 | 156 | if (myAllowHex && myBuffer[i] == 'x') { 157 | return locateHexEscapeSequence(start, i) 158 | } 159 | 160 | return if (myBuffer[i] == 'u') { 161 | locateUnicodeEscapeSequence(start, i) 162 | } else { 163 | i + 1 164 | } 165 | } 166 | while (i < myBufferEnd) { 167 | if (myBuffer[i] == '\\') { 168 | return i 169 | } 170 | if (myState == AFTER_FIRST_QUOTE.toInt() && myBuffer[i] == myQuoteChar) { 171 | if (i + 1 == myBufferEnd) myState = AFTER_LAST_QUOTE.toInt() 172 | return i + 1 173 | } 174 | i++ 175 | myState = AFTER_FIRST_QUOTE.toInt() 176 | } 177 | 178 | return i 179 | } 180 | 181 | protected fun locateHexEscapeSequence(start: Int, i: Int): Int { 182 | var i = i 183 | i++ 184 | while (i < start + 4) { 185 | if (i == myBufferEnd || myBuffer[i] == '\n' || myBuffer[i] == myQuoteChar) { 186 | return i 187 | } 188 | i++ 189 | } 190 | return i 191 | } 192 | 193 | protected fun locateUnicodeEscapeSequence(start: Int, i: Int): Int { 194 | var i = i 195 | i++ 196 | while (i < start + 6) { 197 | if (i == myBufferEnd || myBuffer[i] == '\n' || myBuffer[i] == myQuoteChar) { 198 | return i 199 | } 200 | i++ 201 | } 202 | return i 203 | } 204 | 205 | override fun advance() { 206 | myLastState = myState 207 | myStart = myEnd 208 | myEnd = locateToken(myStart) 209 | } 210 | 211 | override fun getBufferSequence() = myBuffer 212 | override fun getBufferEnd() = myBufferEnd 213 | 214 | override fun toString(): String { 215 | return "StringLiteralLexer {myAllowHex=$myAllowHex, myAllowOctal=$myAllowOctal, mySeenEscapedSpacesOnly=$mySeenEscapedSpacesOnly, myAdditionalValidEscapes='$myAdditionalValidEscapes${'\''.toString()}, myCanEscapeEolOrFramingSpaces=$myCanEscapeEolOrFramingSpaces, myOriginalLiteralToken=$myOriginalLiteralToken, myQuoteChar=$myQuoteChar, myBufferEnd=$myBufferEnd, myLastState=$myLastState, myState=$myState, myEnd=$myEnd, myStart=$myStart, myToken=" + (if (!::myBuffer.isInitialized || myEnd < myStart || myEnd > myBuffer.length) 216 | null 217 | else 218 | myBuffer.subSequence( 219 | myStart, 220 | myEnd)) + '}'.toString() 221 | } 222 | 223 | companion object { 224 | private const val AFTER_FIRST_QUOTE: Short = 1 225 | private const val AFTER_LAST_QUOTE: Short = 2 226 | 227 | const val NO_QUOTE_CHAR = (-1).toChar() 228 | } 229 | } 230 | /** 231 | * @param canEscapeEolOrFramingSpaces true if following sequences are acceptable 232 | * '\' in the end of the buffer (meaning escaped end of line) or 233 | * '\ ' (escaped space) in the beginning and in the end of the buffer (meaning escaped space, to avoid auto trimming on load) 234 | */ --------------------------------------------------------------------------------