Skip to content
Closed
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
17 changes: 16 additions & 1 deletion app/src/main/java/io/github/rosemoe/sora/app/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import io.github.rosemoe.sora.event.SelectionChangeEvent
import io.github.rosemoe.sora.event.SideIconClickEvent
import io.github.rosemoe.sora.event.TextSizeChangeEvent
import io.github.rosemoe.sora.graphics.inlayHint.ColorInlayHintRenderer
import io.github.rosemoe.sora.graphics.inlayHint.GhostTextInlayHintRenderer
import io.github.rosemoe.sora.graphics.inlayHint.TextInlayHintRenderer
import io.github.rosemoe.sora.lang.EmptyLanguage
import io.github.rosemoe.sora.lang.JavaLanguageSpec
Expand All @@ -68,8 +69,10 @@ import io.github.rosemoe.sora.lang.analysis.AsyncIncrementalAnalyzeManager
import io.github.rosemoe.sora.lang.diagnostic.DiagnosticRegion
import io.github.rosemoe.sora.lang.styling.color.ConstColor
import io.github.rosemoe.sora.lang.styling.inlayHint.ColorInlayHint
import io.github.rosemoe.sora.lang.styling.inlayHint.GhostTextInlayHint
import io.github.rosemoe.sora.lang.styling.inlayHint.InlayHintProvider
import io.github.rosemoe.sora.lang.styling.inlayHint.TextInlayHint
import io.github.rosemoe.sora.lang.styling.inlayHint.addGhostText
import io.github.rosemoe.sora.langs.java.JavaLanguage
import io.github.rosemoe.sora.langs.monarch.MonarchColorScheme
import io.github.rosemoe.sora.langs.monarch.MonarchLanguage
Expand Down Expand Up @@ -225,10 +228,12 @@ class MainActivity : AppCompatActivity() {
binding.editor.apply {
registerInlayHintRenderers(
TextInlayHintRenderer.DefaultInstance,
ColorInlayHintRenderer.DefaultInstance
ColorInlayHintRenderer.DefaultInstance,
GhostTextInlayHintRenderer.DefaultInstance
)
typefaceText = typeface
props.stickyScroll = true
props.foldingEnabled = true
setLineSpacing(2f, 1.1f)
nonPrintablePaintingFlags =
CodeEditor.FLAG_DRAW_WHITESPACE_LEADING or CodeEditor.FLAG_DRAW_LINE_SEPARATOR or CodeEditor.FLAG_DRAW_WHITESPACE_IN_SELECTION or CodeEditor.FLAG_DRAW_SOFT_WRAP
Expand Down Expand Up @@ -588,10 +593,20 @@ class MainActivity : AppCompatActivity() {

demoInlayHintProvider?.let { binding.editor.unregisterInlayHintProvider(it) }
if ("big_sample" !in name) {
val lineCount = binding.editor.text.lineCount
demoInlayHintProvider = InlayHintProvider { container ->
container.add(ColorInlayHint(10, 30, ConstColor("#f44336")))
container.add(TextInlayHint(29, 7, "^DigitTens"))
container.add(TextInlayHint(100, 1, "^Numbers"))
// Ghost text demo (single line)
container.add(GhostTextInlayHint(11, 40, " // RED color value"))
// Ghost text demo (multi-line)
container.addGhostText(
100, 0,
" private static final int GHOST_VALUE = 42;\n" +
" // end of ghost text demo",
lineCount
)
}
binding.editor.registerInlayHintProvider(demoInlayHintProvider!!)
} else {
Expand Down
5 changes: 2 additions & 3 deletions editor/src/main/java/io/github/rosemoe/sora/I18nConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
/**
* Map editor built-in string resources to your given string resource. Editor string resource has
* limited i18n function, as it only contains English and Chinese.
* <p>
* Note that you should configure this before creating editor instances
*
* <p>Note that you should configure this before creating editor instances
*
* @author Rosemoe
*/
Expand Down Expand Up @@ -64,5 +64,4 @@ public static int getResourceId(int resId) {
public static String getString(@NonNull Context context, int resId) {
return context.getString(getResourceId(resId));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2025 Rosemoe
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*
* Please contact Rosemoe by email 2073412493@qq.com if you need
* additional information or have any questions
******************************************************************************/

package io.github.rosemoe.sora.graphics.inlayHint

import android.graphics.Canvas
import io.github.rosemoe.sora.graphics.InlayHintRenderParams
import io.github.rosemoe.sora.graphics.Paint
import io.github.rosemoe.sora.lang.styling.inlayHint.GhostTextInlayHint
import io.github.rosemoe.sora.lang.styling.inlayHint.InlayHint
import io.github.rosemoe.sora.widget.schemes.EditorColorScheme

/**
* A ghost text inlay hint renderer.
*
* The ghost text is drawn with the same font size as the editor text but in
* the original text color with 50% transparency, so it looks like a faded
* preview of the suggested text.
*
* @see GhostTextInlayHint
* @author Ghost
*/
open class GhostTextInlayHintRenderer : InlayHintRenderer() {

companion object {
val DefaultInstance = GhostTextInlayHintRenderer()

/**
* Alpha value of 50% transparency
*/
private const val GHOST_TEXT_ALPHA = 0x80
}

protected val localPaint = Paint().also { it.isAntiAlias = true }

override val typeName: String
get() = GhostTextInlayHint.TYPE_NAME

override fun onMeasure(
inlayHint: InlayHint,
paint: Paint,
params: InlayHintRenderParams
): Float {
return paint.measureText((inlayHint as? GhostTextInlayHint)?.text ?: "")
}

override fun onRender(
inlayHint: InlayHint,
canvas: Canvas,
paint: Paint,
params: InlayHintRenderParams,
colorScheme: EditorColorScheme,
measuredWidth: Float
) {
val ghostText = inlayHint as? GhostTextInlayHint ?: return
localPaint.typeface = paint.typeface
localPaint.textSize = paint.textSize
val baseColor = colorScheme.getColor(EditorColorScheme.TEXT_NORMAL)
localPaint.color = (baseColor and 0x00FFFFFF) or (GHOST_TEXT_ALPHA shl 24)
canvas.drawText(ghostText.text, 0f, params.textBaseline.toFloat(), localPaint)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*******************************************************************************
* sora-editor - the awesome code editor for Android
* https://github.com/Rosemoe/sora-editor
* Copyright (C) 2020-2025 Rosemoe
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*
* Please contact Rosemoe by email 2073412493@qq.com if you need
* additional information or have any questions
******************************************************************************/

package io.github.rosemoe.sora.lang.styling.inlayHint

/**
* A ghost text inlay hint. The [text] is rendered inline like a suggestion
* preview, in the original text color with 50% transparency.
*
* Ghost text is a suggestion preview displayed in place of the text that would
* be inserted if the user accepts the suggestion. It does not modify the
* document content.
*
* For multi-line suggestions, use [split] to create one hint per line, or
* [addGhostText] to add a multi-line ghost text to an [InlayHintsContainer].
* The first line is anchored at the given (line, column) and following lines
* are anchored to the start of the following lines.
*
* @see io.github.rosemoe.sora.graphics.inlayHint.GhostTextInlayHintRenderer
* @author Ghost
*/
open class GhostTextInlayHint(
line: Int,
column: Int,
val text: String,
displaySide: CharacterSide = CharacterSide.LEFT
) : InlayHint(line, column, TYPE_NAME, displaySide) {

companion object {
const val TYPE_NAME = "ghost-text"

/**
* Split a (potentially) multi-line ghost text into a list of
* [GhostTextInlayHint], one per line.
*
* The first line is anchored at the given [line] and [column], while
* every following line is anchored to the start (column 0) of the
* corresponding following line.
*
* A trailing newline in [text] does not produce an empty trailing hint.
* If [lineCount] is given, hints anchored past the end of the document
* are dropped instead of silently referencing a non-existent line.
*/
fun split(
line: Int,
column: Int,
text: String,
lineCount: Int = Int.MAX_VALUE
): List<GhostTextInlayHint> {
var lines = text.split('\n')
if (lines.size > 1 && lines.last().isEmpty()) {
lines = lines.dropLast(1)
}
val maxLine = (lineCount - 1).coerceAtLeast(0)
return lines.mapIndexedNotNull { index, lineText ->
val targetLine = line + index
if (targetLine > maxLine) {
null
} else {
GhostTextInlayHint(
line = targetLine,
column = if (index == 0) column else 0,
text = lineText
)
}
}
}
}

}

/**
* Add a (potentially) multi-line ghost text to this container.
*
* @see GhostTextInlayHint.split
*/
fun InlayHintsContainer.addGhostText(
line: Int,
column: Int,
text: String,
lineCount: Int = Int.MAX_VALUE
) {
GhostTextInlayHint.split(line, column, text, lineCount).forEach(::add)
}
Loading