Skip to content

Commit 127d1ac

Browse files
.
2 parents 9351ca1 + f212f03 commit 127d1ac

File tree

10 files changed

+220
-35
lines changed

10 files changed

+220
-35
lines changed

app/src/main/java/io/github/rosemoe/sora/app/MainActivity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ class MainActivity : AppCompatActivity() {
216216
props.stickyScroll = true
217217
setLineSpacing(2f, 1.1f)
218218
nonPrintablePaintingFlags =
219-
CodeEditor.FLAG_DRAW_WHITESPACE_LEADING or CodeEditor.FLAG_DRAW_LINE_SEPARATOR or CodeEditor.FLAG_DRAW_WHITESPACE_IN_SELECTION
219+
CodeEditor.FLAG_DRAW_WHITESPACE_LEADING or CodeEditor.FLAG_DRAW_LINE_SEPARATOR or CodeEditor.FLAG_DRAW_WHITESPACE_IN_SELECTION or CodeEditor.FLAG_DRAW_SOFT_WRAP
220220
// Update display dynamically
221221
// Use CodeEditor#subscribeEvent to add listeners of different events to editor
222222
subscribeAlways<SelectionChangeEvent> { updatePositionText() }

editor-lsp/src/main/java/io/github/rosemoe/sora/lsp/client/languageserver/requestmanager/DefaultRequestManager.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ class DefaultRequestManager(
438438
override fun documentHighlight(params: DocumentHighlightParams): CompletableFuture<List<DocumentHighlight>>? {
439439
return if (checkStatus()) {
440440
try {
441-
if (serverCapabilities.documentHighlightProvider != null) textDocumentService.documentHighlight(
441+
if (serverCapabilities.documentHighlightProvider?.left == true || serverCapabilities.documentHighlightProvider?.right != null) textDocumentService.documentHighlight(
442442
params
443443
) else null
444444
} catch (e: Exception) {
@@ -451,7 +451,7 @@ class DefaultRequestManager(
451451
override fun documentSymbol(params: DocumentSymbolParams): CompletableFuture<List<Either<SymbolInformation, DocumentSymbol>>>? {
452452
return if (checkStatus()) {
453453
try {
454-
if (serverCapabilities.documentSymbolProvider != null) textDocumentService.documentSymbol(
454+
if (serverCapabilities.documentSymbolProvider?.left == true || serverCapabilities.documentSymbolProvider?.right != null) textDocumentService.documentSymbol(
455455
params
456456
) else null
457457
} catch (e: Exception) {
@@ -464,7 +464,7 @@ class DefaultRequestManager(
464464
override fun formatting(params: DocumentFormattingParams): CompletableFuture<List<TextEdit>>? {
465465
return if (checkStatus()) {
466466
try {
467-
if (serverCapabilities.documentFormattingProvider != null) textDocumentService.formatting(
467+
if (serverCapabilities.documentFormattingProvider?.left == true || serverCapabilities.documentFormattingProvider?.right != null) textDocumentService.formatting(
468468
params
469469
) else null
470470
} catch (e: Exception) {
@@ -479,7 +479,7 @@ class DefaultRequestManager(
479479
override fun rangeFormatting(params: DocumentRangeFormattingParams): CompletableFuture<List<TextEdit>>? {
480480
return if (checkStatus()) {
481481
try {
482-
if (serverCapabilities.documentRangeFormattingProvider != null) textDocumentService.rangeFormatting(
482+
if (serverCapabilities.documentRangeFormattingProvider?.left == true || serverCapabilities.documentRangeFormattingProvider?.right != null) textDocumentService.rangeFormatting(
483483
params
484484
) else null
485485
} catch (e: Exception) {
@@ -647,7 +647,7 @@ class DefaultRequestManager(
647647
override fun foldingRange(params: FoldingRangeRequestParams): CompletableFuture<List<FoldingRange>>? {
648648
return if (checkStatus()) {
649649
try {
650-
if (serverCapabilities.foldingRangeProvider != null) textDocumentService.foldingRange(
650+
if (serverCapabilities.foldingRangeProvider?.left == true || serverCapabilities.foldingRangeProvider?.right != null) textDocumentService.foldingRange(
651651
params
652652
) else null
653653
} catch (e: Exception) {

editor/src/main/java/io/github/rosemoe/sora/lang/completion/comparators.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,13 +287,13 @@ fun List<CompletionItem>.highlightMatchLabel(colorSchema: EditorColorScheme?): L
287287

288288
for (index in score.matches.indices.reversed()) {
289289
val matchIndex = score.matches[index]
290-
290+
291291
// Skip invalid indices
292292
if (matchIndex < 0 || matchIndex >= spannable.length) continue
293-
293+
294294
val end = (matchIndex + 1).coerceAtMost(spannable.length)
295295
if (end <= matchIndex) continue
296-
296+
297297
try {
298298
spannable.setSpan(
299299
ForegroundColorSpan(matchedColor),

editor/src/main/java/io/github/rosemoe/sora/widget/CodeEditor.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,12 @@ public class CodeEditor extends View implements ContentListener, Formatter.Forma
232232
* @see #setNonPrintablePaintingFlags(int)
233233
*/
234234
public static final int FLAG_DRAW_WHITESPACE_IN_SELECTION = 1 << 6;
235+
/**
236+
* Draw soft-wrap indicator in text
237+
*
238+
* @see #setNonPrintablePaintingFlags(int)
239+
*/
240+
public static final int FLAG_DRAW_SOFT_WRAP = 1 << 7;
235241
/*
236242
* Internal state identifiers of action mode
237243
*/
@@ -2058,9 +2064,14 @@ public int getNonPrintablePaintingFlags() {
20582064
* @see #FLAG_DRAW_WHITESPACE_FOR_EMPTY_LINE
20592065
* @see #FLAG_DRAW_LINE_SEPARATOR
20602066
* @see #FLAG_DRAW_WHITESPACE_IN_SELECTION
2067+
* @see #FLAG_DRAW_SOFT_WRAP
20612068
*/
20622069
public void setNonPrintablePaintingFlags(int flags) {
2070+
int oldFlags = nonPrintableOptions;
20632071
this.nonPrintableOptions = flags;
2072+
if ((oldFlags & FLAG_DRAW_SOFT_WRAP) != (flags & FLAG_DRAW_SOFT_WRAP)) {
2073+
createLayout();
2074+
}
20642075
invalidate();
20652076
}
20662077

editor/src/main/java/io/github/rosemoe/sora/widget/DirectAccessProps.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ public class DirectAccessProps implements Serializable {
304304
*/
305305
@InvalidateRequired
306306
@FloatRange(from = 0.0f, to = 1.0f)
307-
public final float miniMarkerSizeFactor = 0.85f;
307+
public final float miniMarkerSizeFactor = 0.45f;
308308

309309
/**
310310
* Specify editor behavior when line number is clicked.

editor/src/main/java/io/github/rosemoe/sora/widget/EditorRenderer.java

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import android.graphics.Color;
3636
import android.graphics.DashPathEffect;
3737
import android.graphics.Path;
38+
import android.graphics.PorterDuff;
3839
import android.graphics.Rect;
3940
import android.graphics.RectF;
4041
import android.graphics.RenderNode;
@@ -55,6 +56,7 @@
5556
import java.util.List;
5657
import java.util.Objects;
5758

59+
import io.github.rosemoe.sora.R;
5860
import io.github.rosemoe.sora.annotations.UnsupportedUserUsage;
5961
import io.github.rosemoe.sora.graphics.BubbleHelper;
6062
import io.github.rosemoe.sora.graphics.BufferedDrawPoints;
@@ -503,7 +505,7 @@ protected void drawSingleTextLine(Canvas canvas, int line, float offsetX, float
503505
int nonPrintableFlags = editor.getNonPrintablePaintingFlags();
504506
// Draw hard wrap
505507
if ((nonPrintableFlags & CodeEditor.FLAG_DRAW_LINE_SEPARATOR) != 0) {
506-
drawMiniGraph(canvas, offsetX, -1, "↵");
508+
drawMiniGraph(canvas, offsetX, -1, editor.getContext().getDrawable(R.drawable.line_break));
507509
}
508510
paintGeneral.setTextSkewX(0);
509511
paintGeneral.setFakeBoldText(false);
@@ -1146,6 +1148,7 @@ protected void drawRows(Canvas canvas, float offset, LongArrayList postDrawLineN
11461148
int leadingWhitespaceEnd = 0;
11471149
int trailingWhitespaceStart = 0;
11481150
float circleRadius = 0f;
1151+
float miniGraphWidth = editor.isWordwrap() && (editor.getNonPrintablePaintingFlags() & CodeEditor.FLAG_DRAW_SOFT_WRAP) != 0 ? getMiniGraphWidth() : 0f;
11491152
var composingPosition = editor.inputConnection.composingText.isComposing() && editor.inputConnection.composingText.startIndex >= 0 && editor.inputConnection.composingText.startIndex < content.length() ? content.getIndexer().getCharPosition(editor.inputConnection.composingText.startIndex) : null;
11501153
var composingLength = editor.inputConnection.composingText.endIndex - editor.inputConnection.composingText.startIndex;
11511154
var draggingSelection = editor.getEventHandler().draggingSelection;
@@ -1236,6 +1239,7 @@ protected void drawRows(Canvas canvas, float offset, LongArrayList postDrawLineN
12361239
// Get visible region on the line
12371240
long charPos = findDesiredVisibleChar(offset3, line, rowInf.startColumn, rowInf.endColumn);
12381241
float paintingOffset = CharPosDesc.getPixelWidthOrOffset(charPos) - offset2;
1242+
paintingOffset += miniGraphWidth;
12391243

12401244
// Draw matched text background
12411245
if (matchedPositions.size() > 0) {
@@ -1336,6 +1340,12 @@ protected void drawRows(Canvas canvas, float offset, LongArrayList postDrawLineN
13361340
long charPos = findDesiredVisibleChar(offset3, line, rowInf.startColumn, rowInf.endColumn);
13371341
int firstVisibleChar = CharPosDesc.getTextOffset(charPos);
13381342
float paintingOffset = CharPosDesc.getPixelWidthOrOffset(charPos) - offset2;
1343+
if (!rowInf.isLeadingRow) {
1344+
if ((editor.getNonPrintablePaintingFlags() & CodeEditor.FLAG_DRAW_SOFT_WRAP) != 0) {
1345+
drawMiniGraph(canvas, offset, row, editor.getContext().getDrawable(R.drawable.softwrap_left));
1346+
}
1347+
paintingOffset += miniGraphWidth;
1348+
}
13391349
charPos = findDesiredVisibleChar(editor.getWidth() - paintingOffset, line, firstVisibleChar, rowInf.endColumn, rowInf.startColumn, true);
13401350
int lastVisibleChar = CharPosDesc.getTextOffset(charPos);
13411351

@@ -1462,7 +1472,9 @@ protected void drawRows(Canvas canvas, float offset, LongArrayList postDrawLineN
14621472

14631473
// Draw hard wrap
14641474
if (lastVisibleChar == columnCount && (nonPrintableFlags & CodeEditor.FLAG_DRAW_LINE_SEPARATOR) != 0) {
1465-
drawMiniGraph(canvas, paintingOffset, row, "↵");
1475+
drawMiniGraph(canvas, paintingOffset, row, editor.getContext().getDrawable(R.drawable.line_break));
1476+
} else if (lastVisibleChar != columnCount && editor.isWordwrap() && (nonPrintableFlags & CodeEditor.FLAG_DRAW_SOFT_WRAP) != 0) {
1477+
drawMiniGraph(canvas, paintingOffset, row, editor.getContext().getDrawable(R.drawable.softwrap_right));
14661478
}
14671479
} else {
14681480
paintingOffset = offset + editor.getRenderContext().getRenderNodeHolder().drawLineHardwareAccelerated(canvas, line, offset, editor.getRowTop(line) - editor.getOffsetY()) - editor.getDpUnit() * 20;
@@ -1726,14 +1738,36 @@ protected void drawWhitespacesOnCanvas(Canvas canvas, float offset, int line, fl
17261738
}
17271739
}
17281740

1741+
public float getMiniGraphWidth() {
1742+
float height = editor.getRowHeightOfText() * editor.getProps().miniMarkerSizeFactor;
1743+
var graph = editor.getContext().getDrawable(R.drawable.line_break);
1744+
if (graph == null) {
1745+
return 0;
1746+
}
1747+
int w = graph.getIntrinsicWidth(), h = graph.getIntrinsicHeight();
1748+
if (w <= 0 || h <= 0 || height <= 0) {
1749+
return 0f;
1750+
}
1751+
return height * ((float) w / h);
1752+
}
1753+
17291754
/**
17301755
* Draw small characters as graph
17311756
*/
1732-
protected void drawMiniGraph(Canvas canvas, float offset, int row, String graph) {
1733-
// Draw
1734-
paintGraph.setColor(editor.getColorScheme().getColor(EditorColorScheme.NON_PRINTABLE_CHAR));
1757+
protected void drawMiniGraph(Canvas canvas, float offset, int row, Drawable graph) {
17351758
float baseline = row == -1 ? (editor.getRowBottom(0) - metricsGraph.descent) : (editor.getRowBottom(row) - editor.getOffsetY() - metricsGraph.descent);
1736-
canvas.drawText(graph, 0, graph.length(), offset, baseline, paintGraph);
1759+
float height = editor.getRowHeightOfText() * editor.getProps().miniMarkerSizeFactor;
1760+
if (height <= 0 || graph == null) {
1761+
return;
1762+
}
1763+
int w = graph.getIntrinsicWidth(), h = graph.getIntrinsicHeight();
1764+
if (w <= 0 || h <= 0) {
1765+
return;
1766+
}
1767+
float width = height * ((float) w / h);
1768+
graph.setColorFilter(editor.getColorScheme().getColor(EditorColorScheme.NON_PRINTABLE_CHAR), PorterDuff.Mode.SRC_ATOP);
1769+
graph.setBounds((int) offset, (int) (baseline - height), (int) (offset + width), (int) baseline);
1770+
graph.draw(canvas);
17371771
}
17381772

17391773
protected int getRowTopForBackground(int row) {
@@ -2445,6 +2479,9 @@ protected List<TextDisplayPosition> getTextRegionPositions(int start, int end) {
24452479
position.row = i;
24462480
var line = content.getLine(row.lineIndex);
24472481
position.left = measureText(line, row.lineIndex, row.startColumn, startOnRow - row.startColumn);
2482+
if (!row.isLeadingRow && (editor.getNonPrintablePaintingFlags() & CodeEditor.FLAG_DRAW_SOFT_WRAP) != 0) {
2483+
position.left += getMiniGraphWidth();
2484+
}
24482485
position.right = position.left + measureText(line, row.lineIndex, startOnRow, endOnRow - startOnRow);
24492486
position.startColumn = startOnRow;
24502487
position.endColumn = endOnRow;
@@ -2474,10 +2511,15 @@ protected void patchTextRegions(Canvas canvas, float textOffset, List<TextDispla
24742511
}
24752512
var startCol = position.startColumn;
24762513
var endCol = position.endColumn;
2514+
var rowRegion = editor.getLayout().getRowAt(position.row);
2515+
var limitCol = rowRegion.endColumn;
24772516
var lineText = getLine(line);
24782517
var column = lineText.length();
24792518
canvas.save();
24802519
var horizontalOffset = textOffset;
2520+
if ((editor.getNonPrintablePaintingFlags() & CodeEditor.FLAG_DRAW_SOFT_WRAP) != 0 && !rowRegion.isLeadingRow) {
2521+
horizontalOffset += getMiniGraphWidth();
2522+
}
24812523
boolean first = true;
24822524
// Find spans to draw
24832525
Span nextSpan = null;
@@ -2493,6 +2535,7 @@ protected void patchTextRegions(Canvas canvas, float textOffset, List<TextDispla
24932535
var spanStart = Math.max(span.getColumn(), position.rowStart);
24942536
var sharedStart = Math.max(startCol, spanStart);
24952537
var spanEnd = nextSpan == null ? column : nextSpan.getColumn();
2538+
spanEnd = Math.min(limitCol, spanEnd);
24962539
spanEnd = Math.min(column, spanEnd); // Spans can be corrupted
24972540
if (spanEnd <= position.startColumn) {
24982541
continue;
@@ -2705,24 +2748,6 @@ public long findDesiredVisibleChar(float target, int lineIndex, int start, int e
27052748
return result;
27062749
}
27072750

2708-
/**
2709-
* Find first visible character
2710-
*
2711-
* @return Character position description, {@link CharPosDesc}
2712-
*/
2713-
@UnsupportedUserUsage
2714-
public long findFirstVisibleCharForWordwrap(float target, int lineIndex, int start, int end, int contextStart, Paint paint) {
2715-
if (start >= end) {
2716-
return CharPosDesc.make(end, 0);
2717-
}
2718-
var gtr = GraphicTextRow.obtain(basicDisplayMode);
2719-
gtr.set(content, lineIndex, contextStart, end, sSpansForWordwrap, paint, editor.getRenderContext());
2720-
gtr.disableCache();
2721-
var res = gtr.findOffsetByAdvance(start, target);
2722-
gtr.recycle();
2723-
return res;
2724-
}
2725-
27262751
@UnsupportedUserUsage
27272752
public float[] computeLineAdvances(int line, Paint paint) {
27282753
var gtr = GraphicTextRow.obtain(basicDisplayMode);

editor/src/main/java/io/github/rosemoe/sora/widget/layout/WordwrapLayout.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
public class WordwrapLayout extends AbstractLayout {
6060

6161
private final int width;
62+
private final float miniGraphWidth;
6263
private final boolean antiWordBreaking;
6364
private List<RowRegion> rowTable;
6465

@@ -69,7 +70,9 @@ public WordwrapLayout(@NonNull CodeEditor editor, @NonNull Content text, boolean
6970
if (clearCache) {
7071
rowTable.clear();
7172
}
72-
width = editor.getWidth() - (int) (editor.measureTextRegionOffset() + editor.getTextPaint().measureText("a"));
73+
miniGraphWidth = (editor.getNonPrintablePaintingFlags() & CodeEditor.FLAG_DRAW_SOFT_WRAP) != 0 ?
74+
editor.getRenderer().getMiniGraphWidth() : 0f;
75+
width = editor.getWidth() - (int) (editor.measureTextRegionOffset() + editor.getTextPaint().measureText("a")) - (int) miniGraphWidth * 2;
7376
breakAllLines();
7477
}
7578

@@ -379,6 +382,9 @@ public long getCharPositionForLayoutOffset(float xOffset, float yOffset) {
379382
int row = (int) (yOffset / editor.getRowHeight());
380383
row = Math.max(0, Math.min(row, rowTable.size() - 1));
381384
RowRegion region = rowTable.get(row);
385+
if (region.startColumn != 0) {
386+
xOffset -= miniGraphWidth;
387+
}
382388
int column = BidiLayout.horizontalIndex(editor, this, text, region.line, region.startColumn, region.endColumn, xOffset);
383389
return IntPair.pack(region.line, column);
384390
}
@@ -412,6 +418,9 @@ public float[] getCharLayoutOffset(int line, int column, float[] dest) {
412418
}
413419
dest[0] = editor.getRowBottom(row);
414420
dest[1] = BidiLayout.horizontalOffset(editor, this, text, region.line, region.startColumn, region.endColumn, column);
421+
if (region.startColumn != 0) {
422+
dest[1] += miniGraphWidth;
423+
}
415424
} else {
416425
dest[0] = dest[1] = 0;
417426
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?xml version="1.0" encoding="utf-8"?><!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2+
~ sora-editor - the awesome code editor for Android
3+
~ https://github.com/Rosemoe/sora-editor
4+
~ Copyright (C) 2020-2025 Rosemoe
5+
~
6+
~ This library is free software; you can redistribute it and/or
7+
~ modify it under the terms of the GNU Lesser General Public
8+
~ License as published by the Free Software Foundation; either
9+
~ version 2.1 of the License, or (at your option) any later version.
10+
~
11+
~ This library is distributed in the hope that it will be useful,
12+
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
~ Lesser General Public License for more details.
15+
~
16+
~ You should have received a copy of the GNU Lesser General Public
17+
~ License along with this library; if not, write to the Free Software
18+
~ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19+
~ USA
20+
~
21+
~ Please contact Rosemoe by email 2073412493@qq.com if you need
22+
~ additional information or have any questions
23+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
24+
25+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
26+
android:width="50dp"
27+
android:height="80dp"
28+
android:viewportWidth="50"
29+
android:viewportHeight="80">
30+
31+
<path
32+
android:pathData="M40,25l 0 25 a3,3 0 0,1 -3,3 l-28 0"
33+
android:strokeWidth="6"
34+
android:fillColor="#00000000"
35+
android:strokeColor="#000000" />
36+
37+
<path
38+
android:pathData="M8,53 m15,-15l-15 15l15,15"
39+
android:strokeWidth="6"
40+
android:fillColor="#00000000"
41+
android:strokeColor="#000000" />
42+
43+
</vector>

0 commit comments

Comments
 (0)