iamacup / react-native-markdown-display

React Native 100% compatible CommonMark renderer
MIT License
599 stars 172 forks source link

Multiple new lines / line breaks are ignored #49

Closed jayshah123 closed 4 years ago

jayshah123 commented 4 years ago

using following:

const copy = `
# h1 Heading 8-)
## h2 Heading 8-)
### h3 Heading 8-)

Just some randome text fto read here

\n\n\n

* one
* twoo
* threee
`;

const bodyStyles = StyleSheet.create({
  // eslint-disable-next-line react-native/no-unused-styles
   paragraph: {
     color: 'white',
     fontSize: 14,
     borderWidth: 1,
     borderColor: 'black',
     margin: 0,
     marginTop: 0,
     marginBottom: 0,
  },
  // eslint-disable-next-line react-native/no-unused-styles
  listUnorderedItemIcon: {
    color: 'white',
    fontSize: 14,
    lineHeight: 14,
    borderWidth: 1,
    borderColor: 'black',
    textAlign: 'center',
    alignSelf: 'center',
  },
  listItem: {
     borderColor: 'blue',
    borderWidth: 1,
  },
  listUnordered: {
    borderColor: 'yellow',
    borderWidth: 1,
  },
  listUnorderedItem: {
    borderColor: 'cyan',
    borderWidth: 1,
  }
});

There is no space between text and list during rendering.

jayshah123 commented 4 years ago
Screen Shot 2019-12-20 at 5 22 10 PM
jayshah123 commented 4 years ago

I see the libarary styles having hardbreak and softbreak (https://github.com/iamacup/react-native-markdown-display/blob/master/src/lib/renderRules.js#L152)

but i think the parser options should be specify that right? https://github.com/iamacup/react-native-markdown-display/blob/master/src/index.js#L140

vs

https://github.com/markdown-it/markdown-it/blob/master/README.md#init-with-presets-and-options

iamacup commented 4 years ago

so you can specify breaks by doing something like this:

import Markdown from 'react-native-markdown-display';
import MarkdownIt from 'react-native-markdown-display/src/MarkdownIt';

...

<Markdown
  markdownit={
    MarkdownIt({
      breaks: true,
    })
  }
>
  {copy}
</Markdown>

...

but something wierd is happening...

const copy = `
first line
second line
\
\
\
\
tada
`;

should output this

but we get something like this:

image

however if we examine the generated tree with this

const mdi = MarkdownIt({ breaks: true });
console.log(mdi.parse(copy, {}));

we see the \ are being translated badly it would seem by markdown it.... which is strange.

I need to do some more tests

MuhammadMattiullah commented 4 years ago

@iamacup any update on this issue ? Input

**Hello**

_This is me_

Regards

When i am rendering this string in markdown display it ignores all the breaks and combine all the lines together like Output Hello_This is me_Regards

Code:

  <Markdown
    markdownit={
      MarkdownIt({ typographer: true, linkify: true,breaks: true,
      }).disable(['blockquote', 'list', 'code'])
    }
    onLinkPress={(url) => onLinkPress(url)}
    style={{ link: { color: '#007BC7' } }}
    rules={rules}
  >
    {body}
  </Markdown>
iamacup commented 4 years ago

What you are asking about is an unrelated issue, this issue is specifically about multiple successive linebreaks not all linebreaks,

Can you provide a full code example for your issue? Like what have you done to the rules etc.

MuhammadMattiullah commented 4 years ago

@iamacup Thankyou for ur response!

const rules = {
  body: (node, children, parent, styles) => children,
  heading1: (node, children, parent, styles) => (
    <Text>
      {children}
      {'\n'}
    </Text>
  ),
  heading2: (node, children, parent, styles) => (
    <Text>
      {' '}
      {children}
      {'\n'}
    </Text>
  ),
  heading3: (node, children, parent, styles) => (
    <Text>
      {' '}
      {children}
      {'\n'}
    </Text>
  ),
  paragraph: (node, children, parent, styles) => children,
};

export const MarkdownText = ({ body, onLinkPress }) => (
  <Markdown
    markdownit={
      MarkdownIt({ typographer: true, linkify: true,breaks: true,
      }).disable(['blockquote', 'list', 'code'])
    }
    onLinkPress={(url) => onLinkPress(url)}
    style={{ link: { color: '#007BC7' } }}
    rules={rules}
  >
    {body}
  </Markdown>
);

This is the code block. I want to handle multiple successive linebreaks in this scenario. Kindly guide me how can i achieve this

I want to show the return content from markdown in the Text Component of React Native thats why i have defined the rules for body and paragraph that it should return me the children instead of wrapping in some view as Text component can't render Views

iamacup commented 4 years ago

So - here is the lo-down on this issue:

  1. https://markdown-it.github.io/#md3=%7B%22source%22%3A%22This%20first%20line%5Cn%5C%5C%5Cn%5C%5C%5Cn%5C%5C%5Cn%5C%5C%5Cn%5C%5C%5CnThis%20seccond%20line%22%2C%22defaults%22%3A%7B%22html%22%3Afalse%2C%22xhtmlOut%22%3Afalse%2C%22breaks%22%3Atrue%2C%22langPrefix%22%3A%22language-%22%2C%22linkify%22%3Atrue%2C%22typographer%22%3Atrue%2C%22_highlight%22%3Afalse%2C%22_strict%22%3Afalse%2C%22_view%22%3A%22debug%22%7D%7D - this is an example of markdown-it apparently parsing the new lines correctly with breaks: true

image

This outputs the following JSON from the parser - markdownit.parse(...):

[
  {
    "type": "paragraph_open",
    "tag": "p",
    "attrs": null,
    "map": [
      0,
      7
    ],
    "nesting": 1,
    "level": 0,
    "children": null,
    "content": "",
    "markup": "",
    "info": "",
    "meta": null,
    "block": true,
    "hidden": false
  },
  {
    "type": "inline",
    "tag": "",
    "attrs": null,
    "map": [
      0,
      7
    ],
    "nesting": 0,
    "level": 1,
    "children": [
      {
        "type": "text",
        "tag": "",
        "attrs": null,
        "map": null,
        "nesting": 0,
        "level": 0,
        "children": null,
        "content": "This first line",
        "markup": "",
        "info": "",
        "meta": null,
        "block": false,
        "hidden": false
      },
      {
        "type": "softbreak",
        "tag": "br",
        "attrs": null,
        "map": null,
        "nesting": 0,
        "level": 0,
        "children": null,
        "content": "",
        "markup": "",
        "info": "",
        "meta": null,
        "block": false,
        "hidden": false
      },
      {
        "type": "hardbreak",
        "tag": "br",
        "attrs": null,
        "map": null,
        "nesting": 0,
        "level": 0,
        "children": null,
        "content": "",
        "markup": "",
        "info": "",
        "meta": null,
        "block": false,
        "hidden": false
      },
      {
        "type": "hardbreak",
        "tag": "br",
        "attrs": null,
        "map": null,
        "nesting": 0,
        "level": 0,
        "children": null,
        "content": "",
        "markup": "",
        "info": "",
        "meta": null,
        "block": false,
        "hidden": false
      },
      {
        "type": "hardbreak",
        "tag": "br",
        "attrs": null,
        "map": null,
        "nesting": 0,
        "level": 0,
        "children": null,
        "content": "",
        "markup": "",
        "info": "",
        "meta": null,
        "block": false,
        "hidden": false
      },
      {
        "type": "hardbreak",
        "tag": "br",
        "attrs": null,
        "map": null,
        "nesting": 0,
        "level": 0,
        "children": null,
        "content": "",
        "markup": "",
        "info": "",
        "meta": null,
        "block": false,
        "hidden": false
      },
      {
        "type": "hardbreak",
        "tag": "br",
        "attrs": null,
        "map": null,
        "nesting": 0,
        "level": 0,
        "children": null,
        "content": "",
        "markup": "",
        "info": "",
        "meta": null,
        "block": false,
        "hidden": false
      },
      {
        "type": "text",
        "tag": "",
        "attrs": null,
        "map": null,
        "nesting": 0,
        "level": 0,
        "children": null,
        "content": "This seccond line",
        "markup": "",
        "info": "",
        "meta": null,
        "block": false,
        "hidden": false
      }
    ],
    "content": "This first line\n\\\n\\\n\\\n\\\n\\\nThis seccond line",
    "markup": "",
    "info": "",
    "meta": null,
    "block": true,
    "hidden": false
  },
  {
    "type": "paragraph_close",
    "tag": "p",
    "attrs": null,
    "map": null,
    "nesting": -1,
    "level": 0,
    "children": null,
    "content": "",
    "markup": "",
    "info": "",
    "meta": null,
    "block": true,
    "hidden": false
  }
]
  1. this 'exact same code' produces a different tree without the breaks:
import React from 'react';
import { SafeAreaView, ScrollView, StatusBar, Text } from 'react-native';

import Markdown, { MarkdownIt, hasParents } from 'react-native-markdown-display';

const markdownItInstance = MarkdownIt({linkify: true, typographer: true, breaks: true});

const copy = `
This first line

This seccond line
`;

const tree = markdownItInstance.parse(copy, {});

console.log(tree);
console.log(JSON.stringify(tree));

const App: () => React$Node = () => {
  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView>
        <ScrollView
          contentInsetAdjustmentBehavior="automatic"
          style={{height: '100%'}}
        >
            <Markdown
              markdownit={markdownItInstance}
              debugPrintTree
            >
              {copy}
            </Markdown>
        </ScrollView>
      </SafeAreaView>
    </>
  );
};

export default App;

outputs the following tree from markdown-it:

image

[
   {
      "type":"paragraph_open",
      "tag":"p",
      "attrs":null,
      "map":[
         1,
         2
      ],
      "nesting":1,
      "level":0,
      "children":null,
      "content":"",
      "markup":"",
      "info":"",
      "meta":null,
      "block":true,
      "hidden":false
   },
   {
      "type":"inline",
      "tag":"",
      "attrs":null,
      "map":[
         1,
         2
      ],
      "nesting":0,
      "level":1,
      "children":[
         {
            "type":"text",
            "tag":"",
            "attrs":null,
            "map":null,
            "nesting":0,
            "level":0,
            "children":null,
            "content":"This first line",
            "markup":"",
            "info":"",
            "meta":null,
            "block":false,
            "hidden":false
         }
      ],
      "content":"This first line",
      "markup":"",
      "info":"",
      "meta":null,
      "block":true,
      "hidden":false
   },
   {
      "type":"paragraph_close",
      "tag":"p",
      "attrs":null,
      "map":null,
      "nesting":-1,
      "level":0,
      "children":null,
      "content":"",
      "markup":"",
      "info":"",
      "meta":null,
      "block":true,
      "hidden":false
   },
   {
      "type":"paragraph_open",
      "tag":"p",
      "attrs":null,
      "map":[
         8,
         9
      ],
      "nesting":1,
      "level":0,
      "children":null,
      "content":"",
      "markup":"",
      "info":"",
      "meta":null,
      "block":true,
      "hidden":false
   },
   {
      "type":"inline",
      "tag":"",
      "attrs":null,
      "map":[
         8,
         9
      ],
      "nesting":0,
      "level":1,
      "children":[
         {
            "type":"text",
            "tag":"",
            "attrs":null,
            "map":null,
            "nesting":0,
            "level":0,
            "children":null,
            "content":"This seccond line",
            "markup":"",
            "info":"",
            "meta":null,
            "block":false,
            "hidden":false
         }
      ],
      "content":"This seccond line",
      "markup":"",
      "info":"",
      "meta":null,
      "block":true,
      "hidden":false
   },
   {
      "type":"paragraph_close",
      "tag":"p",
      "attrs":null,
      "map":null,
      "nesting":-1,
      "level":0,
      "children":null,
      "content":"",
      "markup":"",
      "info":"",
      "meta":null,
      "block":true,
      "hidden":false
   }
]

So - this is either:

I don't think it is the first two - so as a result i am going to close this issue as a problem beyond the scope of this project.

Any help further debugging this would be appreciated - if anyone wants to investigate and raise issues on other projects thats also fine.

You can use <br> to make new lines - like this:

image

import React from 'react';
import { SafeAreaView, ScrollView, StatusBar, Text } from 'react-native';

import Markdown, { MarkdownIt, hasParents } from 'react-native-markdown-display';

const markdownItInstance = MarkdownIt({typographer: true, html: true});

const copy = `
Top one
<br>
<br>
<br>
Bottom one

 | Option | Description |
 | ------ | ----------- |
 | data   | path to data files to supply the data<br><br><br>that will be passed into templates. <br><br><br><br> cool.|
`;

const rules = {
  html_inline:(node, children, parent, styles) => {
    // we check that the parent array contans a td because <br> in paragraph setting will create a html_inlinde surrounded by a soft break, try removing the clause to see what happens (double spacing on the <br> between 'top one' and 'bottom one')
    if(node.content.trim() === '<br>' && hasParents(parent, 'td')) {
      return <Text key={node.key}>{'\n'}</Text>
    }

    return null;
  }
}

const App: () => React$Node = () => {
  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView>
        <ScrollView
          contentInsetAdjustmentBehavior="automatic"
          style={{height: '100%'}}
        >
            <Markdown
              rules={rules}
              markdownit={markdownItInstance}
              debugPrintTree
            >
              {copy}
            </Markdown>
        </ScrollView>
      </SafeAreaView>
    </>
  );
};

export default App;
Hlyan107 commented 1 year ago

Hey, I added a custom function to push 'br' tag every between \n\n. Ta-da! You no longer need to type 'br' tag in your textInput, it will automatically transform to fit the library.

Now as in Iamacup's example,

import React from 'react';
import { SafeAreaView, ScrollView, StatusBar, Text } from 'react-native';

import Markdown, { MarkdownIt, hasParents } from 'react-native-markdown-display';

const markdownItInstance = MarkdownIt({typographer: true, html: true});

//this one saved the month for me
 //to push <br> between \n\n
  const pushBr = str => {
    let afterSplit = str
      .split('\n')
      .map(e => {
        if (e === '') return '<br>';
        return e;
      })
      .join('\n');
    return afterSplit;
  };

const copy = `
Top one

Bottom one

 | Option | Description |
 | ------ | ----------- |
 | data   | path to data files to supply the data<br><br><br>that will be passed into templates. <br><br><br><br> cool.|
`;

const rules = {
  html_inline:(node, children, parent, styles) => {
    // we check that the parent array contans a td because <br> in paragraph setting will create a html_inlinde surrounded by a soft break, try removing the clause to see what happens (double spacing on the <br> between 'top one' and 'bottom one')
    if(node.content.trim() === '<br>' && hasParents(parent, 'td')) {
      return <Text key={node.key}>{'\n'}</Text>
    }

    return null;
  }
}

const App: () => React$Node = () => {
  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView>
        <ScrollView
          contentInsetAdjustmentBehavior="automatic"
          style={{height: '100%'}}
        >
            <Markdown
              rules={rules}
              markdownit={markdownItInstance}
              debugPrintTree
            >
              {pushBr(copy)}
            </Markdown>
        </ScrollView>
      </SafeAreaView>
    </>
  );
};

export default App;
isaachinman commented 8 months ago

@iamacup I see that you investigated this issue thoroughly and closed it, but after reading through carefully, it's still not clear to me what the best fix is.

Yes, I can pass this:

markdownit={
  MarkdownIt({
    breaks: true,
    html: true,
  })
}

And manually do:

const content = originalContent.replace(/\n\n/g, '\n<br>\n<br>\n')

But this is really not what I want to do, as this will also insert <br> tags into code fences, and all kinds of other places where I don't want them.

I'd really rather not go down the rabbithole of trying to write some complex function to replace multi-line breaks in some places and not in others.

Do you have any up-to-date advice on how to support multiple line breaks, without actually modifying the source content itself?