Skip to content

Commit

Permalink
Merge pull request #244 from newhinton/feature/noid/markdownblocks
Browse files Browse the repository at this point in the history
Feature/noid/markdownblocks
  • Loading branch information
stefan-niedermann authored Jan 20, 2024
2 parents 6f12272 + 9b913b8 commit 1b26be2
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Locale;
Expand All @@ -35,6 +36,7 @@
import io.noties.markwon.Markwon;
import it.niedermann.android.markdown.model.EListType;
import it.niedermann.android.markdown.model.SearchSpan;
import it.niedermann.android.markdown.remoteviews.RemoteViewElement;

@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public class MarkdownUtil {
Expand All @@ -58,6 +60,71 @@ private MarkdownUtil() {
// Util class
}


/**
* {@link RemoteView}s have a limited subset of supported classes to maintain compatibility with many different launchers.
* <p>
* This function seperates render-able markdown into elements that can be displayed by a widget natively.
* Currently supported are: Markdowntext, Checkbox
*
* @return ArrayList<RemoteViewElement> List of Elements from the note.
*/
public static ArrayList<RemoteViewElement> getRenderedElementsForRemoteView(@NonNull Context context, @NonNull String content) {

ArrayList<RemoteViewElement> remoteViews = new ArrayList<>();
StringBuilder currentLineBlock = new StringBuilder();
int startLine = 0;

final String[] lines = content.split("\n");
boolean isInFencedCodeBlock = false;
int fencedCodeBlockSigns = 0;
for (int i = 0; i < lines.length; i++) {
final var matcher = PATTERN_CODE_FENCE.matcher(lines[i]);
if (matcher.find()) {
final String fence = matcher.group(1);
if (fence != null) {
int currentFencedCodeBlockSigns = fence.length();
if (isInFencedCodeBlock) {
if (currentFencedCodeBlockSigns == fencedCodeBlockSigns) {
isInFencedCodeBlock = false;
fencedCodeBlockSigns = 0;
}
} else {
isInFencedCodeBlock = true;
fencedCodeBlockSigns = currentFencedCodeBlockSigns;
}
}
}
if (!isInFencedCodeBlock) {
if (isCheckboxLine(lines[i])) {
// if the first line is a checkbox, this will be an empty markdown block. It will also end in line -1.
var endline = i-1;
if(endline<0) {
endline = 0;
}
remoteViews.add(new RemoteViewElement(RemoteViewElement.TYPE_TEXT, currentLineBlock.toString(), startLine, endline));
currentLineBlock = new StringBuilder();
startLine = i+1;

boolean isChecked = false;
for (EListType listType : EListType.values()) {
if(lineStartsWithCheckedCheckbox(lines[i], listType)) {
isChecked = true;
}
}
if(isChecked) {
remoteViews.add(new RemoteViewElement(RemoteViewElement.TYPE_CHECKBOX_CHECKED, lines[i], i, i));
} else {
remoteViews.add(new RemoteViewElement(RemoteViewElement.TYPE_CHECKBOX_UNCHECKED, lines[i], i, i));
}
continue;
}
}
currentLineBlock.append(lines[i]).append("\n");
}
return remoteViews;
}

/**
* {@link RemoteView}s have a limited subset of supported classes to maintain compatibility with many different launchers.
* <p>
Expand Down Expand Up @@ -161,14 +228,21 @@ private static String runForEachCheckbox(@NonNull String markdownString, @NonNul
}
}
if (!isInFencedCodeBlock) {
if (lineStartsWithCheckbox(lines[i]) && lines[i].trim().length() > EListType.DASH.checkboxChecked.length()) {
if (isCheckboxLine(lines[i])) {
lines[i] = map.apply(lines[i]);
}
}
}
return TextUtils.join("\n", lines);
}

public static boolean isCheckboxLine(String line) {
if (lineStartsWithCheckbox(line) && line.trim().length() > EListType.DASH.checkboxChecked.length()) {
return true;
}
return false;
}

public static int getStartOfLine(@NonNull CharSequence s, int cursorPosition) {
int startOfLine = cursorPosition;
while (startOfLine > 0 && s.charAt(startOfLine - 1) != '\n') {
Expand Down Expand Up @@ -256,10 +330,21 @@ public static boolean lineStartsWithCheckbox(@NonNull String line) {
}

public static boolean lineStartsWithCheckbox(@NonNull String line, @NonNull EListType listType) {
return lineStartsWithCheckedCheckbox(line, listType) | lineStartsWithUncheckedCheckbox(line, listType);
}

public static boolean lineStartsWithCheckedCheckbox(@NonNull String line, @NonNull EListType listType) {
final String trimmedLine = line.trim();
return (trimmedLine.startsWith(listType.checkboxChecked) || trimmedLine.startsWith(listType.checkboxCheckedUpperCase));
}

public static boolean lineStartsWithUncheckedCheckbox(@NonNull String line, @NonNull EListType listType) {
final String trimmedLine = line.trim();
return (trimmedLine.startsWith(listType.checkboxUnchecked) || trimmedLine.startsWith(listType.checkboxChecked) || trimmedLine.startsWith(listType.checkboxCheckedUpperCase));
return (trimmedLine.startsWith(listType.checkboxUnchecked));
}



/**
* @return the number of the ordered list item if the line is an ordered list, otherwise -1.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package it.niedermann.android.markdown.remoteviews

import java.util.ArrayList

class RemoteViewElement(
val type: Int,
val currentLineBlock: String,
val blockStartsInLine: Int,
val blockEndsInLine: Int
) {

companion object {
const val TYPE_TEXT = 0
const val TYPE_CHECKBOX_CHECKED = 1
const val TYPE_CHECKBOX_UNCHECKED = 2
}

override fun toString(): String {
var elements = StringBuilder()

if(type == TYPE_TEXT) {
elements.append("TYPE_TEXT").append("\n")
}
if(type == TYPE_CHECKBOX_CHECKED) {
elements.append("TYPE_CHECKBOX_CHECKED").append("\n")
}
if(type == TYPE_CHECKBOX_UNCHECKED) {
elements.append("TYPE_CHECKBOX_UNCHECKED").append("\n")
}

elements.append("Content:").append("\n")
elements.append(currentLineBlock).append("\n")
elements.append("Started from $blockStartsInLine to $blockEndsInLine").append("\n")
return elements.toString()
}
}

0 comments on commit 1b26be2

Please sign in to comment.