Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions app/src/main/java/org/autojs/autojs/ui/edit/EditorMenu.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import android.text.InputType;
import android.text.TextUtils;
import android.view.MenuItem;
import android.widget.Toast;
import androidx.annotation.Nullable;
import com.afollestad.materialdialogs.MaterialDialog;
import io.reactivex.android.schedulers.AndroidSchedulers;
Expand All @@ -16,6 +17,7 @@
import org.autojs.autojs.script.JavaScriptFileSource;
import org.autojs.autojs.ui.common.NotAskAgainDialog;
import org.autojs.autojs.ui.edit.editor.CodeEditor;
import org.autojs.autojs.ui.edit.editor.CodeEditor.StackFrame;
import org.autojs.autojs.ui.main.scripts.EditableFileInfoDialogManager;
import org.autojs.autojs.ui.project.BuildActivity;
import org.autojs.autojs.util.ClipboardUtils;
Expand Down Expand Up @@ -92,6 +94,10 @@ private boolean onJumpOptionsSelected(MenuItem item) {
jumpToLine();
return true;
}
if (itemId == R.id.action_jump_to_error_line) {
jumpToErrorLine();
return true;
}
if (itemId == R.id.action_jump_to_start) {
mEditor.jumpToStart();
return true;
Expand Down Expand Up @@ -311,6 +317,19 @@ private void jumpToLine() {
.subscribe(this::showJumpDialog);
}

private void jumpToErrorLine() {
if (mEditor.hasErrorLine()) {
CodeEditor.StackFrame frame = mEditor.getNextStackFrame();
mEditor.jumpTo(frame.getLineNumber(), frame.getColumnNumber());
int total = mEditor.getStackFrameCount();
int current = mEditor.getCurrentStackIndex() + 1;
String info = mContext.getString(R.string.text_stack_frame_info, current, total, frame.getFunctionName(), frame.getLineNumber() + 1);
Toast.makeText(mContext, info, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(mContext, R.string.text_no_error_line, Toast.LENGTH_SHORT).show();
}
}

private void showJumpDialog(final int lineCount) {
String hint = "1 - " + lineCount;
MaterialDialog.Builder builder = new MaterialDialog.Builder(mContext)
Expand Down
50 changes: 50 additions & 0 deletions app/src/main/java/org/autojs/autojs/ui/edit/EditorView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import org.autojs.autojs.ui.edit.completion.CodeCompletionBar.OnHintClickListene
import org.autojs.autojs.ui.edit.debug.DebugBar
import org.autojs.autojs.ui.edit.editor.CodeEditor
import org.autojs.autojs.ui.edit.editor.CodeEditor.CheckedPatternSyntaxException
import org.autojs.autojs.ui.edit.editor.CodeEditor.StackFrame
import org.autojs.autojs.ui.edit.keyboard.FunctionsKeyboardHelper
import org.autojs.autojs.ui.edit.keyboard.FunctionsKeyboardView
import org.autojs.autojs.ui.edit.keyboard.FunctionsKeyboardView.ClickCallback
Expand Down Expand Up @@ -145,6 +146,9 @@ class EditorView : LinearLayout, OnHintClickListener, ClickCallback, ToolbarFrag
val msg = intent.getStringExtra(EXTRA_EXCEPTION_MESSAGE)
val line = intent.getIntExtra(EXTRA_EXCEPTION_LINE_NUMBER, -1)
val col = intent.getIntExtra(EXTRA_EXCEPTION_COLUMN_NUMBER, 0)
// Parse stack trace and set stack frames for navigation
val stackFrames = parseStackTrace(msg, line, col)
editor.setStackFrames(stackFrames)
if (line >= 1) {
editor.jumpTo(line - 1, col)
}
Expand Down Expand Up @@ -754,6 +758,52 @@ class EditorView : LinearLayout, OnHintClickListener, ClickCallback, ToolbarFrag
mTmpSavedFileForRunning?.deleteOnExit()
}

/**
* Parse stack trace from error message
* Rhino stack trace format: "at funcName (/path/file.js:line:col)" or "at /path/file.js:line:col"
*/
private fun parseStackTrace(msg: String?, mainLine: Int, mainCol: Int): ArrayList<CodeEditor.StackFrame> {
val frames = ArrayList<CodeEditor.StackFrame>()
if (msg == null) {
if (mainLine >= 1) {
frames.add(CodeEditor.StackFrame("<error>", mainLine - 1, mainCol))
}
return frames
}

// Add main error line first (from RhinoException)
if (mainLine >= 1) {
frames.add(CodeEditor.StackFrame("<error>", mainLine - 1, mainCol))
}

// Parse stack frames: "at funcName (/path/file.js:line:col)" or "at /path/file.js:line:col"
// Pattern matches both formats
val stackPattern = Regex("""at\s+(?:(\w+)\s+)?\([^)]*?:(\d+):(\d+)\)|at\s+[^:]+:(\d+):(\d+)""")
stackPattern.findAll(msg).forEach { match ->
val (funcName, lineStr, colStr) = when {
match.groupValues[1] != "" -> Triple(
match.groupValues[1],
match.groupValues[2],
match.groupValues[3]
)
match.groupValues[4] != "" -> Triple(
"<anonymous>",
match.groupValues[4],
match.groupValues[5]
)
else -> return@forEach
}
val line = lineStr.toIntOrNull()?.minus(1) ?: return@forEach
val col = colStr.toIntOrNull() ?: 0
// Skip duplicate entries
if (frames.none { it.lineNumber == line && it.columnNumber == col }) {
frames.add(CodeEditor.StackFrame(funcName, line, col))
}
}

return frames
}

companion object {

private val TAG = EditorView::class.java.simpleName
Expand Down
39 changes: 39 additions & 0 deletions app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,45 @@ class CodeEditor : HVScrollView {
var enabled = true
}

/**
* Stack frame for error navigation
* Used to store function name, line number and column number of each call frame
*/
data class StackFrame(
@JvmField val functionName: String,
@JvmField val lineNumber: Int,
@JvmField val columnNumber: Int
)

private val mStackFrames = ArrayList<StackFrame>()
private var mCurrentStackIndex = -1
private var mHasErrorLine = false

fun setStackFrames(frames: ArrayList<StackFrame>) {
mStackFrames.clear()
mStackFrames.addAll(frames)
mCurrentStackIndex = -1
mHasErrorLine = frames.isNotEmpty()
}

fun getNextStackFrame(): StackFrame? {
if (mStackFrames.isEmpty()) return null
mCurrentStackIndex = (mCurrentStackIndex + 1) % mStackFrames.size
return mStackFrames[mCurrentStackIndex]
}

fun getStackFrameCount(): Int = mStackFrames.size

fun getCurrentStackIndex(): Int = mCurrentStackIndex

fun hasErrorLine(): Boolean = mHasErrorLine

fun clearErrorLine() {
mStackFrames.clear()
mCurrentStackIndex = -1
mHasErrorLine = false
}

class CheckedPatternSyntaxException(cause: PatternSyntaxException?) : Exception(cause)

interface BreakpointChangeListener {
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/res/menu/menu_editor.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@
android:title="@string/text_jump_to_line"
app:showAsAction="never" />

<item
android:id="@+id/action_jump_to_error_line"
android:title="@string/text_jump_to_error_line"
app:showAsAction="never" />

<item
android:id="@+id/action_jump_to_start"
android:title="@string/text_jump_to_start"
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/res/values-zh/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,9 @@
<string name="text_jump_to_line">跳转到行</string>
<string name="text_jump_to_line_end">跳转到行尾</string>
<string name="text_jump_to_line_start">跳转到行首</string>
<string name="text_jump_to_error_line">跳转到出错行</string>
<string name="text_no_error_line">没有错误行</string>
<string name="text_stack_frame_info">堆栈 %1$d/%2$d: %3$s() 行 %4$d</string>
<string name="text_jump_to_start">跳转到文件开始</string>
<string name="text_keep_screen_on_when_in_foreground">前台时保持屏幕常亮</string>
<string name="text_key_alias">别名</string>
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,9 @@
<string name="text_jump_to_line">Jump to line</string>
<string name="text_jump_to_line_end">Jump to line end</string>
<string name="text_jump_to_line_start">Jump to line start</string>
<string name="text_jump_to_error_line">Jump to error line</string>
<string name="text_no_error_line">No error line</string>
<string name="text_stack_frame_info">Stack %1$d/%2$d: %3$s() line %4$d</string>
<string name="text_jump_to_start">Jump to start</string>
<string name="text_keep_screen_on_when_in_foreground">Keep screen on when in foreground</string>
<string name="text_key_alias">Alias</string>
Expand Down