Skip to content

Commit

Permalink
Add support for Pandoc-style inline/display math
Browse files Browse the repository at this point in the history
  • Loading branch information
valentjn committed Jan 27, 2021
1 parent 391cf7f commit 45d62f2
Show file tree
Hide file tree
Showing 10 changed files with 515 additions and 2 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@

# Changelog

## 9.1.1 (upcoming)
## 9.2.0 (upcoming)

- Add support for Pandoc-style inline math (`$...$`) and display math (`$$...$$` with the `$$` being at the beginning/end of a Markdown block) to Markdown parser (fixes [vscode-ltex#210](https://github.com/valentjn/vscode-ltex/issues/210))
- Fix false positives for words added by `Add to dictionary` for Slovak rule IDs `MUZSKY_ROD_NEZIV_A`, `ZENSKY_ROD_A`, and `STREDNY_ROD_A` (fixes [vscode-ltex#221](https://github.com/valentjn/vscode-ltex/issues/221))
- Fix BibT<sub>E</sub>X field `seealso` not ignored, ignore `category` and `parent` (see [vscode-ltex#211](https://github.com/valentjn/vscode-ltex/issues/211))
- Disable `UPPERCASE_SENTENCE_START` in BibT<sub>E</sub>X files (see [vscode-ltex#211](https://github.com/valentjn/vscode-ltex/issues/211))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/* Copyright (C) 2020 Julian Valentin, LTeX Development Community
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

package org.bsplines.ltexls.parsing.markdown;

import com.vladsch.flexmark.ast.Paragraph;
import com.vladsch.flexmark.ast.ParagraphContainer;
import com.vladsch.flexmark.util.ast.Block;
import com.vladsch.flexmark.util.ast.BlockContent;
import com.vladsch.flexmark.util.sequence.BasedSequence;
import java.util.List;
import org.jetbrains.annotations.NotNull;

public class LtexMarkdownDisplayMath extends Block implements ParagraphContainer {
private BasedSequence openingMarker = BasedSequence.NULL;
private BasedSequence openingTrailing = BasedSequence.NULL;
private BasedSequence closingMarker = BasedSequence.NULL;
private BasedSequence closingTrailing = BasedSequence.NULL;

@Override
public void getAstExtra(@NotNull StringBuilder out) {
segmentSpanChars(out, this.openingMarker, "open");
segmentSpanChars(out, this.openingTrailing, "openTrail");
segmentSpanChars(out, this.closingMarker, "close");
segmentSpanChars(out, this.closingTrailing, "closeTrail");
}

@Override
public @NotNull BasedSequence[] getSegments() {
return new BasedSequence[] {
this.openingMarker,
this.openingTrailing,
this.closingMarker,
this.closingTrailing
};
}

@Override
public boolean isParagraphEndWrappingDisabled(Paragraph node) {
return ((node == getLastChild()) || (node.getNext() instanceof LtexMarkdownDisplayMath));
}

@Override
public boolean isParagraphStartWrappingDisabled(Paragraph node) {
return ((node == getFirstChild()) || (node.getPrevious() instanceof LtexMarkdownDisplayMath));
}

public LtexMarkdownDisplayMath() {
}

public LtexMarkdownDisplayMath(BasedSequence chars) {
super(chars);
}

public LtexMarkdownDisplayMath(BasedSequence chars, List<BasedSequence> segments) {
super(chars, segments);
}

public LtexMarkdownDisplayMath(BlockContent blockContent) {
super(blockContent);
}

public BasedSequence getOpeningMarker() {
return this.openingMarker;
}

public void setOpeningMarker(BasedSequence openingMarker) {
this.openingMarker = openingMarker;
}

public BasedSequence getClosingMarker() {
return this.closingMarker;
}

public void setClosingMarker(BasedSequence closingMarker) {
this.closingMarker = closingMarker;
}

public BasedSequence getOpeningTrailing() {
return this.openingTrailing;
}

public void setOpeningTrailing(BasedSequence openingTrailing) {
this.openingTrailing = openingTrailing;
}

public BasedSequence getClosingTrailing() {
return this.closingTrailing;
}

public void setClosingTrailing(BasedSequence closingTrailing) {
this.closingTrailing = closingTrailing;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/* Copyright (C) 2020 Julian Valentin, LTeX Development Community
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

package org.bsplines.ltexls.parsing.markdown;

import com.vladsch.flexmark.parser.InlineParser;
import com.vladsch.flexmark.parser.block.AbstractBlockParser;
import com.vladsch.flexmark.parser.block.AbstractBlockParserFactory;
import com.vladsch.flexmark.parser.block.BlockContinue;
import com.vladsch.flexmark.parser.block.BlockParser;
import com.vladsch.flexmark.parser.block.BlockParserFactory;
import com.vladsch.flexmark.parser.block.BlockStart;
import com.vladsch.flexmark.parser.block.CustomBlockParserFactory;
import com.vladsch.flexmark.parser.block.MatchedBlockParser;
import com.vladsch.flexmark.parser.block.ParserState;
import com.vladsch.flexmark.util.ast.Block;
import com.vladsch.flexmark.util.ast.BlockContent;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.util.data.DataHolder;
import com.vladsch.flexmark.util.sequence.BasedSequence;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class LtexMarkdownDisplayMathParser extends AbstractBlockParser {
private static final Pattern DISPLAY_MATH_START_PATTERN = Pattern.compile("\\$\\$(\\s*$)");
private static final Pattern DISPLAY_MATH_END_PATTERN = Pattern.compile("\\$\\$(\\s*$)");

private LtexMarkdownDisplayMath block = new LtexMarkdownDisplayMath();
private @Nullable BlockContent content = new BlockContent();
private boolean hadClose = false;

LtexMarkdownDisplayMathParser(DataHolder options, BasedSequence openMarker,
BasedSequence openTrailing) {
this.block.setOpeningMarker(openMarker);
this.block.setOpeningTrailing(openTrailing);
}

@Override
public Block getBlock() {
return this.block;
}

@Override
public BlockContinue tryContinue(ParserState state) {
if (this.hadClose) return BlockContinue.none();

int index = state.getIndex();
BasedSequence line = state.getLineWithEOL();
Matcher matcher = DISPLAY_MATH_END_PATTERN.matcher(line.subSequence(index));

if (!matcher.matches()) {
return BlockContinue.atIndex(index);
} else {
@Nullable Node lastChild = this.block.getLastChild();

if ((lastChild != null) && (lastChild instanceof LtexMarkdownDisplayMath)) {
BlockParser parser = state.getActiveBlockParser((Block)lastChild);

if ((parser instanceof LtexMarkdownDisplayMathParser)
&& !((LtexMarkdownDisplayMathParser)parser).hadClose) {
return BlockContinue.atIndex(index);
}
}

this.hadClose = true;
this.block.setClosingMarker(state.getLine().subSequence(index, index + 2));
this.block.setClosingTrailing(
state.getLineWithEOL().subSequence(matcher.start(1), matcher.end(1)));

return BlockContinue.atIndex(state.getLineEndIndex());
}
}

@Override
public void addLine(ParserState state, BasedSequence line) {
if (this.content == null) return;
this.content.add(line, state.getIndent());
}

@Override
public void closeBlock(ParserState state) {
if (this.content == null) return;
this.block.setContent(this.content);
this.block.setCharsFromContent();
this.content = null;
}

@Override
public boolean isContainer() {
return false;
}

@Override
public boolean canContain(ParserState state, BlockParser blockParser, Block block) {
return false;
}

@Override
public void parseInlines(InlineParser inlineParser) {
}

public static class Factory implements CustomBlockParserFactory {
@Override
public @Nullable Set<Class<?>> getAfterDependents() {
return null;
}

@Override
public @Nullable Set<Class<?>> getBeforeDependents() {
return null;
}

@Override
public boolean affectsGlobalScope() {
return false;
}

@Override
public @NotNull BlockParserFactory apply(@NotNull DataHolder options) {
return new BlockFactory(options);
}
}

private static class BlockFactory extends AbstractBlockParserFactory {
BlockFactory(DataHolder options) {
super(options);
}

private static boolean haveDisplayMathParser(ParserState state) {
List<BlockParser> parsers = state.getActiveBlockParsers();

for (int i = parsers.size() - 1; i >= 0; i--) {
if (parsers.get(i) instanceof LtexMarkdownDisplayMathParser) return true;
}

return false;
}

@Override
public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
if (!haveDisplayMathParser(state)) {
BasedSequence line = state.getLineWithEOL();
Matcher matcher = DISPLAY_MATH_START_PATTERN.matcher(line);

if (matcher.matches()) {
LtexMarkdownDisplayMathParser parser = new LtexMarkdownDisplayMathParser(
state.getProperties(),
line.subSequence(0, 2),
line.subSequence(matcher.start(1), matcher.end(1)));
return BlockStart.of(parser).atIndex(state.getLineEndIndex());
}
}

return BlockStart.none();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/* Copyright (C) 2020 Julian Valentin, LTeX Development Community
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

package org.bsplines.ltexls.parsing.markdown;

import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.data.DataKey;
import com.vladsch.flexmark.util.data.MutableDataHolder;

public class LtexMarkdownExtension implements Parser.ParserExtension {
public static final DataKey<Boolean> DISPLAY_MATH_PARSER =
new DataKey<>("DISPLAY_MATH_PARSER", true);
public static final DataKey<Boolean> INLINE_MATH_PARSER =
new DataKey<>("INLINE_MATH_PARSER", true);

private LtexMarkdownExtension() {
}

public static LtexMarkdownExtension create() {
return new LtexMarkdownExtension();
}

@Override
public void parserOptions(MutableDataHolder options) {
}

@Override
public void extend(Parser.Builder parserBuilder) {
LtexMarkdownOptions options = new LtexMarkdownOptions(parserBuilder);

if (options.displayMathParser) {
parserBuilder.customBlockParserFactory(new LtexMarkdownDisplayMathParser.Factory());
}

if (options.inlineMathParser) {
parserBuilder.customInlineParserExtensionFactory(new LtexMarkdownInlineMathParser.Factory());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/* Copyright (C) 2020 Julian Valentin, LTeX Development Community
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

package org.bsplines.ltexls.parsing.markdown;

import com.vladsch.flexmark.util.ast.DelimitedNode;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.util.sequence.BasedSequence;
import org.jetbrains.annotations.NotNull;

public class LtexMarkdownInlineMath extends Node implements DelimitedNode {
protected BasedSequence openingMarker = BasedSequence.NULL;
protected BasedSequence text = BasedSequence.NULL;
protected BasedSequence closingMarker = BasedSequence.NULL;

@Override
public @NotNull BasedSequence[] getSegments() {
return new BasedSequence[] { this.openingMarker, this.text, this.closingMarker };
}

@Override
public void getAstExtra(@NotNull StringBuilder out) {
delimitedSegmentSpanChars(out, this.openingMarker, this.text, this.closingMarker, "text");
}

public LtexMarkdownInlineMath() {
}

public LtexMarkdownInlineMath(BasedSequence chars) {
super(chars);
}

public LtexMarkdownInlineMath(BasedSequence openingMarker, BasedSequence text,
BasedSequence closingMarker) {
super(openingMarker.baseSubSequence(
openingMarker.getStartOffset(), closingMarker.getEndOffset()));
this.openingMarker = openingMarker;
this.text = text;
this.closingMarker = closingMarker;
}

public BasedSequence getOpeningMarker() {
return this.openingMarker;
}

public void setOpeningMarker(BasedSequence openingMarker) {
this.openingMarker = openingMarker;
}

public BasedSequence getText() {
return this.text;
}

public void setText(BasedSequence text) {
this.text = text;
}

public BasedSequence getClosingMarker() {
return this.closingMarker;
}

public void setClosingMarker(BasedSequence closingMarker) {
this.closingMarker = closingMarker;
}
}
Loading

0 comments on commit 45d62f2

Please sign in to comment.