finish
This commit is contained in:
parent
c7f8a7c30b
commit
fa9ff80214
11 changed files with 552 additions and 84 deletions
124
.idea/uiDesigner.xml
Normal file
124
.idea/uiDesigner.xml
Normal file
|
@ -0,0 +1,124 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Palette2">
|
||||
<group name="Swing">
|
||||
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
||||
</item>
|
||||
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
|
||||
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
||||
<initial-values>
|
||||
<property name="text" value="Button" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="RadioButton" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="CheckBox" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="Label" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
||||
<preferred-size width="-1" height="20" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
||||
</item>
|
||||
</group>
|
||||
</component>
|
||||
</project>
|
|
@ -28,7 +28,7 @@ compose.desktop {
|
|||
mainClass = "MainKt"
|
||||
|
||||
nativeDistributions {
|
||||
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
|
||||
targetFormats(TargetFormat.Dmg, TargetFormat.Exe, TargetFormat.Deb)
|
||||
packageName = "VariableRelation"
|
||||
packageVersion = "1.0.0"
|
||||
}
|
||||
|
|
|
@ -1,88 +1,182 @@
|
|||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.ButtonColors
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.ExperimentalTextApi
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.Window
|
||||
import androidx.compose.ui.window.application
|
||||
import core.SourceFile
|
||||
import core.*
|
||||
import ui.TextfieldWithLineNumber
|
||||
import utils.*
|
||||
|
||||
@OptIn(ExperimentalTextApi::class)
|
||||
@Composable
|
||||
@Preview
|
||||
fun App() {
|
||||
var text by remember { mutableStateOf("") }
|
||||
var isReady by remember { mutableStateOf(false) }
|
||||
var source: SourceFile? = null
|
||||
val showResult = remember { mutableStateOf(false) }
|
||||
var defineList by remember { mutableStateOf("") }
|
||||
var useList by remember { mutableStateOf("") }
|
||||
var traceTree by remember { mutableStateOf("") }
|
||||
var funcTraceTree by remember { mutableStateOf("") }
|
||||
var page by remember { mutableStateOf(0) }
|
||||
var ttGraph by remember { mutableStateOf("") }
|
||||
var ivGraph by remember { mutableStateOf("") }
|
||||
|
||||
MaterialTheme {
|
||||
Column(
|
||||
when {
|
||||
showResult.value -> {
|
||||
Dialog(onDismissRequest = {
|
||||
showResult.value = false
|
||||
page = 0
|
||||
}) {
|
||||
AlertDialog(
|
||||
title = {
|
||||
Text(
|
||||
text = when (page) {
|
||||
0 -> "函数定义的变量"
|
||||
1 -> "函数内每条语句的定义和使用"
|
||||
2 -> "函数内每个变量 def-use 链"
|
||||
3 -> "函数间每个变量 def-use 链"
|
||||
else -> "出错了"
|
||||
},
|
||||
color = Color.Black,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 24.sp,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
text = when (page) {
|
||||
0 -> defineList
|
||||
1 -> useList
|
||||
2 -> traceTree
|
||||
3 -> funcTraceTree
|
||||
else -> "出错了(ง •̀_•́)ง"
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.wrapContentHeight(),
|
||||
textAlign = TextAlign.Start,
|
||||
color = Color.Black
|
||||
)
|
||||
},
|
||||
onDismissRequest = {
|
||||
showResult.value = false
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
page = (page + 1) % 4
|
||||
}
|
||||
) {
|
||||
Text("下一个")
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
if (page < 2){
|
||||
showResult.value = false
|
||||
}else{
|
||||
if (page == 2){
|
||||
openGraph(ttGraph)
|
||||
}else if (page == 3){
|
||||
openGraph(ivGraph)
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
if (page < 2){
|
||||
"关闭"
|
||||
}else{
|
||||
"查看图像(在线)"
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(5.dp)
|
||||
.wrapContentSize(),
|
||||
shape = RoundedCornerShape(20.dp),
|
||||
backgroundColor = Color(0xffFAF0E6)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
.padding(horizontal = 10.dp, vertical = 5.dp),
|
||||
.background(color = Color(0xFFFBFBFB))
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize().padding(horizontal = 10.dp, vertical = 5.dp),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Text("请输入您的代码")
|
||||
|
||||
TextField(
|
||||
value = text,
|
||||
onValueChange = { text = it },
|
||||
modifier = Modifier.fillMaxWidth(0.95f)
|
||||
.weight(1f),
|
||||
textStyle = TextStyle.Default.copy(fontFamily = FontFamily("monospace"))
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(0.9f)
|
||||
.padding(vertical = 5.dp),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
TextfieldWithLineNumber(
|
||||
modifier = Modifier.weight(1f).fillMaxWidth(),
|
||||
) {
|
||||
Button(
|
||||
modifier = Modifier.padding(vertical = 10.dp),
|
||||
onClick = {
|
||||
val s = SourceFile(text.toString())
|
||||
text = s.content
|
||||
source = s
|
||||
},
|
||||
) {
|
||||
Text("格式化")
|
||||
if (it.isEmpty()) {
|
||||
return@TextfieldWithLineNumber
|
||||
}
|
||||
val sf = SourceFile(it)
|
||||
source = sf
|
||||
try {
|
||||
sf.parseFunction()
|
||||
} catch (e: Exception) {
|
||||
// 分析失败
|
||||
defineList = e.printStackTrace().toString()
|
||||
return@TextfieldWithLineNumber
|
||||
}
|
||||
|
||||
Button(
|
||||
modifier = Modifier.padding(vertical = 10.dp),
|
||||
onClick = {
|
||||
source = SourceFile(text.toString())
|
||||
isReady = true
|
||||
},
|
||||
enabled = !isReady
|
||||
) {
|
||||
Text("分析")
|
||||
defineList =
|
||||
try {
|
||||
val def = getDefineList(sf.getDef())
|
||||
def.ifEmpty {
|
||||
"分析失败:可能输入有错误!"
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace().toString()
|
||||
}
|
||||
useList = try {
|
||||
getUseList(sf.getUse())
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace().toString()
|
||||
}
|
||||
val relations = mutableMapOf<String, List<Relation>>()
|
||||
val traceTreeStr = StringBuilder()
|
||||
sf.functions.forEach { func ->
|
||||
traceTreeStr.append(getTraceTreeString(func.getTraceTree().getStringRepr(),func.name))
|
||||
relations.putAll(parseRelation(func.name,func.getTraceTree().getStringRepr()))
|
||||
}
|
||||
traceTree = traceTreeStr.toString()
|
||||
|
||||
funcTraceTree =
|
||||
getInvokeTraceTreeString("main",sf,relations)
|
||||
|
||||
ttGraph = generateGraph(relations)
|
||||
ivGraph = sf.functions.getInvokeGraph("main")
|
||||
|
||||
showResult.value = true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun main() = application {
|
||||
Window(onCloseRequest = ::exitApplication) {
|
||||
|
|
|
@ -127,16 +127,14 @@ fun List<CFunction>.find(name: String): CFunction? {
|
|||
* @param name: 从哪个函数开始查找
|
||||
* @return 特制的 TraceTree
|
||||
*/
|
||||
fun List<CFunction>.getInvokeTree(name: String): List<TraceTree> {
|
||||
fun List<CFunction>.getInvokeTree(name: String): Map<String,List<TraceTree>> {
|
||||
// 先找到开始的函数
|
||||
val func = this.find(name)
|
||||
if (func == null) {
|
||||
return emptyList()
|
||||
}
|
||||
val func = this.find(name) ?: return emptyMap()
|
||||
|
||||
val invokeTrees = mutableListOf<TraceTree>()
|
||||
val args = mutableListOf<String>()
|
||||
var funcName = ""
|
||||
val resultMap = mutableMapOf<String,List<TraceTree>>()
|
||||
|
||||
func.cParser.sentenceList.forEach { sentence ->
|
||||
// 🌰:
|
||||
|
@ -153,9 +151,12 @@ fun List<CFunction>.getInvokeTree(name: String): List<TraceTree> {
|
|||
val tt = function.getTraceTree(info)
|
||||
invokeTrees.addAll(tt)
|
||||
}
|
||||
if (funcName != "") {
|
||||
resultMap[funcName] = invokeTrees
|
||||
args.clear()
|
||||
funcName = ""
|
||||
}
|
||||
}
|
||||
return invokeTrees
|
||||
}
|
||||
return resultMap
|
||||
}
|
|
@ -24,8 +24,8 @@ fun generateGraph(relations: Map<String, List<Relation>>, crossLabelPaths: List<
|
|||
// 处理跨函数
|
||||
for (path in crossLabelPaths) {
|
||||
val parts = path.split(":")
|
||||
if (parts.size == 2) {
|
||||
val subPath = parts[1].split("->") // 栗子: main:z->a->m
|
||||
if (parts.size == 3) {
|
||||
val subPath = parts[2].split("->") // 栗子: A:main:z->a->m
|
||||
if (subPath.size >= 2) {
|
||||
val fromNode = subPath[0] // 这里就是z
|
||||
val toNode = subPath[1] // 下一个就是
|
||||
|
|
|
@ -4,6 +4,8 @@ package core
|
|||
|
||||
import java.io.File
|
||||
import core.CLanguage.Companion.OPERATION.*
|
||||
import utils.getDefineList
|
||||
import utils.getUseList
|
||||
|
||||
class SourceFile {
|
||||
companion object {
|
||||
|
@ -20,7 +22,7 @@ class SourceFile {
|
|||
}
|
||||
|
||||
val content: String
|
||||
private var functions: List<CFunction> = emptyList()
|
||||
var functions: List<CFunction> = emptyList()
|
||||
|
||||
constructor(file: File) {
|
||||
if (!file.isFile) {
|
||||
|
@ -52,8 +54,8 @@ class SourceFile {
|
|||
return result
|
||||
}
|
||||
|
||||
fun getUse(): List<List<String>> {
|
||||
val globalResult = mutableListOf<List<String>>()
|
||||
fun getUse(): Map<String,List<String>> {
|
||||
val globalResult = mutableMapOf<String,List<String>>()
|
||||
functions.forEach {
|
||||
val funcResult = mutableListOf<String>()
|
||||
|
||||
|
@ -97,7 +99,7 @@ class SourceFile {
|
|||
if (useCache.isNotEmpty()) {
|
||||
funcResult.add(useCache.toString())
|
||||
}
|
||||
globalResult.add(funcResult)
|
||||
globalResult[it.name] = funcResult
|
||||
}
|
||||
return globalResult
|
||||
}
|
||||
|
@ -120,4 +122,7 @@ fun main() {
|
|||
println(sourceFile.getUse())
|
||||
println(generateGraph(relations))
|
||||
println(funcs.getInvokeGraph("main"))
|
||||
|
||||
println(getDefineList(sourceFile.getDef()))
|
||||
println(getUseList(sourceFile.getUse()))
|
||||
}
|
|
@ -47,7 +47,12 @@ fun List<TraceTree>.getStringRepr(): String {
|
|||
return result.toString()
|
||||
}
|
||||
|
||||
fun List<TraceTree>.getFuncRepr(func: String): String {
|
||||
val strs = this.getStringRepr()
|
||||
return strs.split('\n').filter { it.startsWith(func) }.joinToString("\n")
|
||||
fun Map<String, List<TraceTree>>.getFuncRepr(func: String): String {
|
||||
val result = StringBuilder()
|
||||
this.forEach { (name, list) ->
|
||||
result.append("$name:")
|
||||
val listRepr = list.getStringRepr()
|
||||
result.append(listRepr.split('\n').filter { it.startsWith(func) }.joinToString("\n"))
|
||||
}
|
||||
return result.toString()
|
||||
}
|
154
src/main/kotlin/ui/TextfieldWithLineNumber.kt
Normal file
154
src/main/kotlin/ui/TextfieldWithLineNumber.kt
Normal file
|
@ -0,0 +1,154 @@
|
|||
package ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.ExperimentalTextApi
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import core.SourceFile
|
||||
import utils.DefaultCode
|
||||
|
||||
@OptIn(ExperimentalTextApi::class)
|
||||
@Composable
|
||||
fun TextfieldWithLineNumber(
|
||||
modifier: Modifier = Modifier,
|
||||
onSubmit: (String) -> Unit
|
||||
) {
|
||||
var linesText by remember { mutableIntStateOf(DefaultCode.hashCode()) }
|
||||
var text by remember { mutableStateOf(DefaultCode.toString()) }
|
||||
|
||||
val showFinishFormatDialog = remember { mutableStateOf(false) }
|
||||
when {
|
||||
showFinishFormatDialog.value -> {
|
||||
FinishFormatDialog {
|
||||
showFinishFormatDialog.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val monospaceTextStyle = TextStyle.Default.copy( // 左右都是等宽字体
|
||||
fontFamily = FontFamily("monospace"),
|
||||
fontSize = 16.sp
|
||||
)
|
||||
|
||||
// 同步左右滚动进度
|
||||
val linesTextScroll = rememberScrollState()
|
||||
val scriptTextScroll = rememberScrollState()
|
||||
|
||||
LaunchedEffect(linesTextScroll.value) {
|
||||
scriptTextScroll.scrollTo(linesTextScroll.value)
|
||||
}
|
||||
LaunchedEffect(scriptTextScroll.value) {
|
||||
linesTextScroll.scrollTo(scriptTextScroll.value)
|
||||
}
|
||||
|
||||
Column(modifier = modifier) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
.weight(1f)
|
||||
.background(color = Color(0xFFfafafa))
|
||||
|
||||
) {
|
||||
// 左边是行号
|
||||
BasicTextField(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.width(12.dp * linesText.toString().length)
|
||||
.verticalScroll(linesTextScroll),
|
||||
value = IntRange(1, linesText).joinToString(separator = "\n"),
|
||||
readOnly = true, // 不能去编辑行号那里
|
||||
textStyle = monospaceTextStyle.copy(
|
||||
textAlign = TextAlign.End,
|
||||
color = Color(0xff69b0c5)
|
||||
),
|
||||
onValueChange = {})
|
||||
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
|
||||
// 输入内容的地方
|
||||
|
||||
BasicTextField(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.weight(1f)
|
||||
.verticalScroll(scriptTextScroll),
|
||||
value = text,
|
||||
textStyle = monospaceTextStyle,
|
||||
onValueChange = { textFieldValue ->
|
||||
val nbLines = textFieldValue.count { it == '\n' } + 1
|
||||
if (nbLines != linesText) linesText = nbLines
|
||||
text = textFieldValue.replace("\t"," ") // \t 显示不了
|
||||
},
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(0.9f)
|
||||
.padding(vertical = 5.dp),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Button(
|
||||
modifier = Modifier.padding(vertical = 10.dp),
|
||||
colors = ButtonDefaults.buttonColors(backgroundColor = Color(0xFFC6E7FF)),
|
||||
onClick = {
|
||||
val s = SourceFile(text)
|
||||
text = s.content
|
||||
showFinishFormatDialog.value = true
|
||||
},
|
||||
) {
|
||||
Text("格式化")
|
||||
}
|
||||
|
||||
Button(
|
||||
modifier = Modifier.padding(vertical = 10.dp),
|
||||
colors = ButtonDefaults.buttonColors(backgroundColor = Color(0xFFD4F6FF)),
|
||||
onClick = { onSubmit(text) },
|
||||
) {
|
||||
Text("分析")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FinishFormatDialog(onDismissRequest: () -> Unit) {
|
||||
Dialog(onDismissRequest = { onDismissRequest() }) {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.height(130.dp)
|
||||
.padding(16.dp),
|
||||
shape = RoundedCornerShape(20.dp),
|
||||
backgroundColor = Color(0xffFFE3E3)
|
||||
) {
|
||||
Text(
|
||||
text = "格式化成功",
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.wrapContentSize(Alignment.Center),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
25
src/main/kotlin/utils/DefaultCode.kt
Normal file
25
src/main/kotlin/utils/DefaultCode.kt
Normal file
|
@ -0,0 +1,25 @@
|
|||
package utils
|
||||
|
||||
object DefaultCode {
|
||||
override fun toString(): String {
|
||||
return """
|
||||
int main(){
|
||||
int x=0,y =0;
|
||||
int z =1;
|
||||
printf("%d\n",z);//用户输入
|
||||
x= A(z);
|
||||
y= x;
|
||||
printf("%d\n",y);//main函数尾部
|
||||
}
|
||||
|
||||
int A(int a){
|
||||
int m = a;
|
||||
return m;
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return this.toString().count { it == '\n' } + 1
|
||||
}
|
||||
}
|
23
src/main/kotlin/utils/OpenBrowser.kt
Normal file
23
src/main/kotlin/utils/OpenBrowser.kt
Normal file
|
@ -0,0 +1,23 @@
|
|||
package utils
|
||||
|
||||
import java.awt.Desktop
|
||||
import java.net.URI
|
||||
import java.util.*
|
||||
|
||||
fun openBrowser(uri: URI) {
|
||||
val osName by lazy(LazyThreadSafetyMode.NONE) { System.getProperty("os.name").lowercase(Locale.getDefault()) }
|
||||
val desktop = Desktop.getDesktop()
|
||||
when {
|
||||
Desktop.isDesktopSupported() && desktop.isSupported(Desktop.Action.BROWSE) -> desktop.browse(uri)
|
||||
"mac" in osName -> Runtime.getRuntime().exec("open $uri")
|
||||
"nix" in osName || "nux" in osName -> Runtime.getRuntime().exec("xdg-open $uri")
|
||||
else -> desktop.browse(uri)
|
||||
}
|
||||
}
|
||||
|
||||
fun openGraph(graph: String){
|
||||
val site = "https://dreampuf.github.io/GraphvizOnline/#"
|
||||
val data = java.net.URLEncoder.encode(graph,"utf-8")
|
||||
.replace("+","%20")
|
||||
openBrowser(URI.create(site+data))
|
||||
}
|
37
src/main/kotlin/utils/stringMagic.kt
Normal file
37
src/main/kotlin/utils/stringMagic.kt
Normal file
|
@ -0,0 +1,37 @@
|
|||
package utils
|
||||
|
||||
import core.*
|
||||
|
||||
fun getDefineList(defList: Map<String, List<String>>): String =
|
||||
// Example:
|
||||
// {main=[x, y, z], A=[a, m]}
|
||||
defList.toString()
|
||||
.replace("{", "")
|
||||
.replace("}", "") // 解决左右括号
|
||||
.replace("=", ":def")
|
||||
|
||||
fun getUseList(useList: Map<String, List<String>>): String =
|
||||
// Example:
|
||||
// {main=[void, x-def, y-def, z-def, z-use, x-def;z-use, y-def;x-use, y-use], A=[a-def, m-def;a-use, m-use]}
|
||||
useList.toString()
|
||||
.replace("{}", "") // 如果是空就直接删了
|
||||
.replace("{", "") // 右括号留着
|
||||
.replace("=", ":{") // 换掉
|
||||
.replace("], ", "]}\n") // 保证不是最后一个
|
||||
.replace(", ", "],[") // 逗号后面有空格
|
||||
.replace(";", "、")
|
||||
|
||||
fun getTraceTreeString(traceTreeStr: String, funcName: String): String {
|
||||
val sb = StringBuilder("$funcName:\n")
|
||||
traceTreeStr.split('\n').forEach {
|
||||
sb.append(" ${it.trim()}\n")
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
fun getInvokeTraceTreeString(funcName: String, sourceFile: SourceFile, relations: Map<String, List<Relation>>): String {
|
||||
val func = sourceFile.functions.find(funcName) ?: return "未找到 $funcName 函数"
|
||||
val originPart = getTraceTreeString(func.getTraceTree().getStringRepr(), func.name)
|
||||
val invokeTree = sourceFile.functions.getInvokeTree(funcName).getFuncRepr(funcName)
|
||||
return originPart + "\n" + invokeTree
|
||||
}
|
Loading…
Reference in a new issue