Skip to content

Commit

Permalink
Merge branch 'master' into scroll_sync
Browse files Browse the repository at this point in the history
  • Loading branch information
guanglinn authored Jan 31, 2025
2 parents cbc6cf4 + abdefb0 commit a310486
Show file tree
Hide file tree
Showing 35 changed files with 624 additions and 246 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
import android.widget.SearchView;
import android.widget.Toast;
Expand All @@ -51,6 +52,7 @@
import net.gsantner.markor.frontend.MarkorDialogFactory;
import net.gsantner.markor.frontend.filebrowser.MarkorFileBrowserFactory;
import net.gsantner.markor.frontend.textview.HighlightingEditor;
import net.gsantner.markor.frontend.textview.LineNumbersTextView;
import net.gsantner.markor.frontend.textview.TextViewUtils;
import net.gsantner.markor.model.AppSettings;
import net.gsantner.markor.model.Document;
Expand Down Expand Up @@ -92,10 +94,12 @@ public static DocumentEditAndViewFragment newInstance(final @NonNull Document do
private HighlightingEditor _hlEditor;
private WebView _webView;
private MarkorWebViewClient _webViewClient;
private FrameLayout _editorHolder;
private ViewGroup _textActionsBar;

private DraggableScrollbarScrollView _primaryScrollView;
private HorizontalScrollView _hsView;
private LineNumbersTextView _lineNumbersView;
private SearchView _menuSearchViewForViewMode;
private Document _document;
private FormatRegistry _format;
Expand Down Expand Up @@ -134,9 +138,12 @@ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
final Activity activity = getActivity();

_hlEditor = view.findViewById(R.id.document__fragment__edit__highlighting_editor);
_editorHolder = view.findViewById(R.id.document__fragment__edit__editor_holder);
_textActionsBar = view.findViewById(R.id.document__fragment__edit__text_actions_bar);
_webView = view.findViewById(R.id.document__fragment_view_webview);
_primaryScrollView = view.findViewById(R.id.document__fragment__edit__content_editor__scrolling_parent);
_lineNumbersView = view.findViewById(R.id.document__fragment__edit__line_numbers_view);

_cu = new MarkorContextUtils(activity);

// Using `if (_document != null)` everywhere is dangerous
Expand All @@ -150,6 +157,9 @@ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
return;
}

_lineNumbersView.setup(_hlEditor);
_lineNumbersView.setLineNumbersEnabled(_appSettings.getDocumentLineNumbersEnabled(_document.path));

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && _appSettings.getSetWebViewFulldrawing()) {
WebView.enableSlowWholeDocumentDraw();
}
Expand Down Expand Up @@ -214,7 +224,6 @@ public void onPageFinished(WebView v) {
_hlEditor.setTextColor(_appSettings.getEditorForegroundColor());
_hlEditor.setGravity(_appSettings.isEditorStartEditingInCenter() ? Gravity.CENTER : Gravity.NO_GRAVITY);
_hlEditor.setHighlightingEnabled(_appSettings.getDocumentHighlightState(_document.path, _hlEditor.getText()));
_hlEditor.setLineNumbersEnabled(_appSettings.getDocumentLineNumbersEnabled(_document.path));
_hlEditor.setAutoFormatEnabled(_appSettings.getDocumentAutoFormatEnabled(_document.path));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Do not need to send contents to accessibility
Expand Down Expand Up @@ -546,7 +555,7 @@ public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
if (saveDocument(false)) {
TextConverterBase converter = FormatRegistry.getFormat(_document.getFormat(), activity, _document).getConverter();
_cu.shareText(getActivity(),
converter.convertMarkup(getTextString(), getActivity(), false, _hlEditor.isLineNumbersEnabled(), _document.file),
converter.convertMarkup(getTextString(), getActivity(), false, _lineNumbersView.isLineNumbersEnabled(), _document.file),
"text/" + (item.getItemId() == R.id.action_share_html ? "html" : "plain")
);
}
Expand Down Expand Up @@ -635,9 +644,9 @@ public void onFsViewerConfig(GsFileBrowserOptions.Options dopt) {
return true;
}
case R.id.action_line_numbers: {
final boolean newState = !_hlEditor.isLineNumbersEnabled();
final boolean newState = !_lineNumbersView.isLineNumbersEnabled();
_appSettings.setDocumentLineNumbersEnabled(_document.path, newState);
_hlEditor.setLineNumbersEnabled(newState);
_lineNumbersView.setLineNumbersEnabled(newState);
updateMenuToggleStates(0);
return true;
}
Expand Down Expand Up @@ -758,7 +767,7 @@ private void updateMenuToggleStates(final int selectedFormatActionId) {
mi.setChecked(_hlEditor.getHighlightingEnabled());
}
if ((mi = _fragmentMenu.findItem(R.id.action_line_numbers)) != null) {
mi.setChecked(_hlEditor.isLineNumbersEnabled());
mi.setChecked(_lineNumbersView.isLineNumbersEnabled());
}
if ((mi = _fragmentMenu.findItem(R.id.action_enable_auto_format)) != null) {
mi.setChecked(_hlEditor.getAutoFormatEnabled());
Expand All @@ -776,19 +785,17 @@ private void updateMenuToggleStates(final int selectedFormatActionId) {
}

private boolean isWrapped() {
return _hsView == null || (_hlEditor.getParent() == _primaryScrollView);
return _hsView == null || (_hlEditor.getParent() == _editorHolder);
}

private void setHorizontalScrollMode(final boolean wrap) {
final Context context = getContext();
if (context != null && _hlEditor != null && isWrapped() != wrap) {

final int[] sel = TextViewUtils.getSelection(_hlEditor);

final boolean hlEnabled = _hlEditor.getHighlightingEnabled();
_hlEditor.setHighlightingEnabled(false);

_primaryScrollView.removeAllViews();
_editorHolder.removeAllViews();
if (_hsView != null) {
_hsView.removeAllViews();
}
Expand All @@ -798,15 +805,14 @@ private void setHorizontalScrollMode(final boolean wrap) {
_hsView.setFillViewport(true);
}
_hsView.addView(_hlEditor);
_primaryScrollView.addView(_hsView);
_editorHolder.addView(_hsView);
} else {
_primaryScrollView.addView(_hlEditor);
_editorHolder.addView(_hlEditor);
}

_hlEditor.setHighlightingEnabled(hlEnabled);

// Run after layout() of immediate parent completes
(wrap ? _primaryScrollView : _hsView).post(() -> TextViewUtils.setSelectionAndShow(_hlEditor, sel));
(wrap ? _editorHolder : _hsView).post(() -> TextViewUtils.setSelectionAndShow(_hlEditor, sel));
}
}

Expand Down Expand Up @@ -880,9 +886,9 @@ private boolean isDisplayedAtMainActivity() {
public void updateViewModeText() {
// Don't let text to view mode crash app
try {
_format.getConverter().convertMarkupShowInWebView(_document, getTextString(), getActivity(), _webView, _nextConvertToPrintMode, _hlEditor.isLineNumbersEnabled());
_format.getConverter().convertMarkupShowInWebView(_document, getTextString(), getActivity(), _webView, _nextConvertToPrintMode, _lineNumbersView.isLineNumbersEnabled());
} catch (OutOfMemoryError e) {
_format.getConverter().convertMarkupShowInWebView(_document, "updateViewModeText getTextString(): OutOfMemory " + e, getActivity(), _webView, _nextConvertToPrintMode, _hlEditor.isLineNumbersEnabled());
_format.getConverter().convertMarkupShowInWebView(_document, "updateViewModeText getTextString(): OutOfMemory " + e, getActivity(), _webView, _nextConvertToPrintMode, _lineNumbersView.isLineNumbersEnabled());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import net.gsantner.markor.format.markdown.MarkdownSyntaxHighlighter;
import net.gsantner.markor.format.markdown.MarkdownTextConverter;
import net.gsantner.markor.format.orgmode.OrgmodeActionButtons;
import net.gsantner.markor.format.orgmode.OrgmodeReplacePatternGenerator;
import net.gsantner.markor.format.orgmode.OrgmodeSyntaxHighlighter;
import net.gsantner.markor.format.orgmode.OrgmodeTextConverter;
import net.gsantner.markor.format.plaintext.PlaintextActionButtons;
Expand Down Expand Up @@ -184,8 +185,8 @@ public static FormatRegistry getFormat(int formatId, @NonNull final Context cont
format._converter = CONVERTER_ORGMODE;
format._highlighter = new OrgmodeSyntaxHighlighter(appSettings);
format._textActions = new OrgmodeActionButtons(context, document);
format._autoFormatInputFilter = new AutoTextFormatter(MarkdownReplacePatternGenerator.formatPatterns);
format._autoFormatTextWatcher = new ListHandler(MarkdownReplacePatternGenerator.formatPatterns);
format._autoFormatInputFilter = new AutoTextFormatter(OrgmodeReplacePatternGenerator.formatPatterns);
format._autoFormatTextWatcher = new ListHandler(OrgmodeReplacePatternGenerator.formatPatterns);
break;
}
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,8 @@ public static Link extract(final CharSequence text, final int pos) {
final int start = m.start(), end = m.end();
if (start <= po && end >= po) {
final boolean isImage = m.group(1) != null;
return new Link(m.group(2), m.group(3), isImage, start, end);
final String link = m.group(3);
return new Link(m.group(2), link == null ? null : link.trim(), isImage, start, end);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public class MarkdownSyntaxHighlighter extends SyntaxHighlighterBase {
private static final int MD_COLOR_LINK = 0xff1ea3fe;
private static final int MD_COLOR_LIST = 0xffdaa521;
private static final int MD_COLOR_QUOTE = 0xff88b04c;
private static final int MD_COLOR_CODEBLOCK = 0x448c8c8c;
private static final int MD_COLOR_CODEBLOCK = 0x2bafafaf;

public MarkdownSyntaxHighlighter(AppSettings as) {
super(as);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import net.gsantner.markor.R;
import net.gsantner.markor.format.ActionButtonBase;
import net.gsantner.markor.format.markdown.MarkdownReplacePatternGenerator;
import net.gsantner.markor.format.orgmode.OrgmodeReplacePatternGenerator;
import net.gsantner.markor.frontend.textview.AutoTextFormatter;
import net.gsantner.markor.model.Document;

Expand Down Expand Up @@ -35,7 +35,10 @@ public List<ActionItem> getFormatActionList() {
new ActionItem(R.string.abid_orgmode_italic, R.drawable.ic_format_italic_black_24dp, R.string.italic),
new ActionItem(R.string.abid_orgmode_strikeout, R.drawable.ic_format_strikethrough_black_24dp, R.string.strikeout),
new ActionItem(R.string.abid_orgmode_underline, R.drawable.ic_format_underlined_black_24dp, R.string.underline),
new ActionItem(R.string.abid_orgmode_code_inline, R.drawable.ic_code_black_24dp, R.string.inline_code)
new ActionItem(R.string.abid_orgmode_code_inline, R.drawable.ic_code_black_24dp, R.string.inline_code),
new ActionItem(R.string.abid_orgmode_h1, R.drawable.format_header_1, R.string.heading_1),
new ActionItem(R.string.abid_orgmode_h2, R.drawable.format_header_2, R.string.heading_2),
new ActionItem(R.string.abid_orgmode_h3, R.drawable.format_header_3, R.string.heading_3)
);
}

Expand All @@ -47,13 +50,39 @@ int getFormatActionsKey() {

@Override
protected void renumberOrderedList() {
// Use markdown format for orgmode too
AutoTextFormatter.renumberOrderedList(_hlEditor.getText(), MarkdownReplacePatternGenerator.formatPatterns);
AutoTextFormatter.renumberOrderedList(_hlEditor.getText(), OrgmodeReplacePatternGenerator.formatPatterns);
}

@Override
public boolean onActionClick(final @StringRes int action) {
switch (action) {
case R.string.abid_orgmode_h1: {
runRegexReplaceAction(OrgmodeReplacePatternGenerator.setOrUnsetHeadingWithLevel(1));
return true;
}
case R.string.abid_orgmode_h2: {
runRegexReplaceAction(OrgmodeReplacePatternGenerator.setOrUnsetHeadingWithLevel(2));
return true;
}
case R.string.abid_orgmode_h3: {
runRegexReplaceAction(OrgmodeReplacePatternGenerator.setOrUnsetHeadingWithLevel(3));
return true;
}
case R.string.abid_common_unordered_list_char: {
final String listChar = _appSettings.getUnorderedListCharacter();
runRegexReplaceAction(OrgmodeReplacePatternGenerator.replaceWithUnorderedListPrefixOrRemovePrefix(listChar));
return true;
}
case R.string.abid_common_checkbox_list: {
final String listChar = _appSettings.getUnorderedListCharacter();
runRegexReplaceAction(OrgmodeReplacePatternGenerator.toggleToCheckedOrUncheckedListPrefix(listChar));
return true;
}
case R.string.abid_common_ordered_list_number: {
runRegexReplaceAction(OrgmodeReplacePatternGenerator.replaceWithOrderedListPrefixOrRemovePrefix());
runRenumberOrderedListIfRequired();
return true;
}
case R.string.abid_orgmode_bold: {
runSurroundAction("*");
return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*#######################################################
*
* Maintained 2018-2025 by Gregor Santner <gsantner AT mailbox DOT org>
* License of this file: Apache 2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
#########################################################*/
package net.gsantner.markor.format.orgmode;

import android.util.Log;

import net.gsantner.markor.format.ActionButtonBase;
import net.gsantner.markor.frontend.textview.AutoTextFormatter;
import net.gsantner.markor.frontend.textview.ReplacePatternGeneratorHelper;
import net.gsantner.opoc.format.GsTextUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

public class OrgmodeReplacePatternGenerator {

// TODO: write tests

public static final Pattern PREFIX_ORDERED_LIST = Pattern.compile("^(\\s*)((\\d+)(\\.|\\))(\\s))");
public static final Pattern PREFIX_ATX_HEADING = Pattern.compile("^(\\s{0,3})(\\*+\\s)");
public static final Pattern PREFIX_CHECKED_LIST = Pattern.compile("^(\\s*)((-|\\+)\\s\\[(X)]\\s)");
public static final Pattern PREFIX_CHECKBOX_LIST = Pattern.compile("^(\\s*)(([-+]\\s\\[)[\\sX](]\\s))");
public static final Pattern PREFIX_UNCHECKED_LIST = Pattern.compile("^(\\s*)((-|\\+)\\s\\[\\s]\\s)");
public static final Pattern PREFIX_UNORDERED_LIST = Pattern.compile("^(\\s*)((-|\\+)\\s)");
public static final Pattern PREFIX_LEADING_SPACE = Pattern.compile("^(\\s*)");

public static final AutoTextFormatter.FormatPatterns formatPatterns = new AutoTextFormatter.FormatPatterns(
OrgmodeReplacePatternGenerator.PREFIX_UNORDERED_LIST,
OrgmodeReplacePatternGenerator.PREFIX_CHECKBOX_LIST,
OrgmodeReplacePatternGenerator.PREFIX_ORDERED_LIST,
2);

public static final Pattern[] PREFIX_PATTERNS = {
PREFIX_ORDERED_LIST,
PREFIX_ATX_HEADING,
PREFIX_CHECKED_LIST,
PREFIX_UNCHECKED_LIST,
// Unordered has to be after checked list. Otherwise checklist will match as an unordered list.
PREFIX_UNORDERED_LIST,
PREFIX_LEADING_SPACE,
};

private final static String ORDERED_LIST_REPLACEMENT = "$11. ";

/**
* Set/unset ATX heading level on each selected line
* <p>
* This routine will make the following conditional changes
* <p>
* Line is heading of same level as requested -> remove heading
* Line is heading of different level that that requested -> add heading of specified level
* Line is not heading -> add heading of specified level
*
* @param level ATX heading level
*/
public static List<ActionButtonBase.ReplacePattern> setOrUnsetHeadingWithLevel(int level) {

List<ActionButtonBase.ReplacePattern> patterns = new ArrayList<>();

String heading = "\\*".repeat(level);

// Replace this exact heading level with nothing
patterns.add(new ActionButtonBase.ReplacePattern("^(\\s{0,3})" + heading + " ", "$1"));

// Replace other headings with commonmark-compatible leading space
patterns.add(new ActionButtonBase.ReplacePattern(OrgmodeReplacePatternGenerator.PREFIX_ATX_HEADING, "$1" + heading + " "));

// Replace all other prefixes with heading
for (final Pattern pp : OrgmodeReplacePatternGenerator.PREFIX_PATTERNS) {
patterns.add(new ActionButtonBase.ReplacePattern(pp, heading + "$1 "));
}

return patterns;
}

public static List<ActionButtonBase.ReplacePattern> replaceWithUnorderedListPrefixOrRemovePrefix(String listChar) {
final String unorderedListReplacement = "$1" + listChar + " ";
return ReplacePatternGeneratorHelper.replaceWithTargetPrefixOrRemove(PREFIX_PATTERNS, PREFIX_UNORDERED_LIST, unorderedListReplacement);
}

public static List<ActionButtonBase.ReplacePattern> toggleToCheckedOrUncheckedListPrefix(String listChar) {
final String unchecked = "$1" + listChar + " [ ] ";
final String checked = "$1" + listChar + " [X] ";
return ReplacePatternGeneratorHelper.replaceWithTargetPatternOrAlternative(PREFIX_PATTERNS, PREFIX_UNCHECKED_LIST, unchecked, checked);
}

public static List<ActionButtonBase.ReplacePattern> replaceWithOrderedListPrefixOrRemovePrefix() {
return ReplacePatternGeneratorHelper.replaceWithTargetPrefixOrRemove(PREFIX_PATTERNS, PREFIX_ORDERED_LIST, ORDERED_LIST_REPLACEMENT);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@
import java.util.regex.Pattern;

public class OrgmodeSyntaxHighlighter extends SyntaxHighlighterBase {
public final static String COMMON_EMPHASIS_PATTERN = "(?<=(\\n|^|\\s|\\{|\\())([%s])(?=\\S)(.*?)\\S\\2(?=(\\n|$|\\s|\\.|,|:|;|-|\\}|\\)))";
public final static String COMMON_EMPHASIS_PATTERN = "(?<=(\\n|^|\\s|\\{|\\())([%s])(?=\\S)(?!\\2+\\2)(.*?)\\S\\2(?=(\\n|$|\\s|\\.|,|:|;|-|\\}|\\)))";
public final static Pattern BOLD = Pattern.compile(String.format(COMMON_EMPHASIS_PATTERN, "*"));
public final static Pattern ITALICS = Pattern.compile(String.format(COMMON_EMPHASIS_PATTERN, "/"));
public final static Pattern STRIKETHROUGH = Pattern.compile(String.format(COMMON_EMPHASIS_PATTERN, "+"));
public final static Pattern UNDERLINE = Pattern.compile(String.format(COMMON_EMPHASIS_PATTERN, "_"));
public final static Pattern CODE_INLINE = Pattern.compile(String.format(COMMON_EMPHASIS_PATTERN, "=~"));
public final static Pattern HEADING = Pattern.compile("(?m)^(\\*+)\\s(.*?)(?=\\n|$)");
public final static Pattern HEADING = Pattern.compile("(?m)^(\\*+) (.*?)(?=\\n|$)");
public final static Pattern BLOCK = Pattern.compile("(?m)(?<=#\\+BEGIN_.{1,15}$\\s)[\\s\\S]*?(?=#\\+END)");
public final static Pattern PREAMBLE = Pattern.compile("(?m)^(#\\+)(.*?)(?=\\n|$)");
public final static Pattern COMMENT = Pattern.compile("(?m)^(#+)\\s(.*?)(?=\\n|$)");
public final static Pattern LIST_UNORDERED = Pattern.compile("(\\n|^)\\s{0,16}([*+-])( \\[[ xX]\\])?(?= )");
public final static Pattern COMMENT = Pattern.compile("(?m)^(#+) (.*?)(?=\\n|$)");
public final static Pattern LIST_UNORDERED = Pattern.compile("(\\n|^)\\s{0,16}([+-])( \\[[ X]\\])?(?= )");
public final static Pattern LIST_ORDERED = Pattern.compile("(?m)^\\s{0,16}(\\d+)(:?\\.|\\))\\s");
public final static Pattern LINK = Pattern.compile("\\[\\[.*?]]|<.*?>|https?://\\S+|\\[.*?]\\[.*?]|\\[.*?]\n");
private static final int ORG_COLOR_HEADING = 0xffef6D00;
Expand Down
Loading

0 comments on commit a310486

Please sign in to comment.