Add angle bracket covered url syntax to mfm (#4483)
* Add angle bracket covered url syntax to mfm * Fix path * Fix match * Fix index
This commit is contained in:
		
							parent
							
								
									0f65b1bcc5
								
							
						
					
					
						commit
						38656103c0
					
				
					 10 changed files with 37 additions and 13 deletions
				
			
		|  | @ -1,5 +1,6 @@ | ||||||
| import { parseFragment, DefaultTreeDocumentFragment } from 'parse5'; | import { parseFragment, DefaultTreeDocumentFragment } from 'parse5'; | ||||||
| import { URL } from 'url'; | import { URL } from 'url'; | ||||||
|  | import { urlRegex } from './prelude'; | ||||||
| 
 | 
 | ||||||
| export function fromHtml(html: string): string { | export function fromHtml(html: string): string { | ||||||
| 	if (html == null) return null; | 	if (html == null) return null; | ||||||
|  | @ -14,7 +15,7 @@ export function fromHtml(html: string): string { | ||||||
| 
 | 
 | ||||||
| 	return text.trim(); | 	return text.trim(); | ||||||
| 
 | 
 | ||||||
| 	function getText(node: any) { | 	function getText(node: any): string { | ||||||
| 		if (node.nodeName == '#text') return node.value; | 		if (node.nodeName == '#text') return node.value; | ||||||
| 
 | 
 | ||||||
| 		if (node.childNodes) { | 		if (node.childNodes) { | ||||||
|  | @ -41,7 +42,7 @@ export function fromHtml(html: string): string { | ||||||
| 
 | 
 | ||||||
| 				// ハッシュタグ / hrefがない / txtがURL
 | 				// ハッシュタグ / hrefがない / txtがURL
 | ||||||
| 				if ((rel && rel.value.match('tag') !== null) || !href || href.value == txt) { | 				if ((rel && rel.value.match('tag') !== null) || !href || href.value == txt) { | ||||||
| 					text += txt; | 					text += txt.match(urlRegex) ? txt : `<${txt}>`; | ||||||
| 				// メンション
 | 				// メンション
 | ||||||
| 				} else if (txt.startsWith('@') && !(rel && rel.value.match(/^me /))) { | 				} else if (txt.startsWith('@') && !(rel && rel.value.match(/^me /))) { | ||||||
| 					const part = txt.split('@'); | 					const part = txt.split('@'); | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| import * as P from 'parsimmon'; | import * as P from 'parsimmon'; | ||||||
| import { createLeaf, createTree } from './types'; | import { createLeaf, createTree, urlRegex } from './prelude'; | ||||||
| import { takeWhile, cumulativeSum } from '../prelude/array'; | import { takeWhile, cumulativeSum } from '../prelude/array'; | ||||||
| import parseAcct from '../misc/acct/parse'; | import parseAcct from '../misc/acct/parse'; | ||||||
| import { toUnicode } from 'punycode'; | import { toUnicode } from 'punycode'; | ||||||
|  | @ -154,9 +154,16 @@ export const mfmLanguage = P.createLanguage({ | ||||||
| 	url: () => { | 	url: () => { | ||||||
| 		return P((input, i) => { | 		return P((input, i) => { | ||||||
| 			const text = input.substr(i); | 			const text = input.substr(i); | ||||||
| 			const match = text.match(/^https?:\/\/[\w\/:%#@\$&\?!\(\)\[\]~\.,=\+\-]+/); | 			const match = text.match(urlRegex); | ||||||
| 			if (!match) return P.makeFailure(i, 'not a url'); | 			let url: string; | ||||||
| 			let url = match[0]; | 			if (!match) { | ||||||
|  | 				const match = text.match(/^<(https?:\/\/.*?)>/); | ||||||
|  | 				if (!match) | ||||||
|  | 					return P.makeFailure(i, 'not a url'); | ||||||
|  | 				url = match[1]; | ||||||
|  | 				i += 2; | ||||||
|  | 			} else | ||||||
|  | 				url = match[0]; | ||||||
| 			url = removeOrphanedBrackets(url); | 			url = removeOrphanedBrackets(url); | ||||||
| 			if (url.endsWith('.')) url = url.substr(0, url.lastIndexOf('.')); | 			if (url.endsWith('.')) url = url.substr(0, url.lastIndexOf('.')); | ||||||
| 			if (url.endsWith(',')) url = url.substr(0, url.lastIndexOf(',')); | 			if (url.endsWith(',')) url = url.substr(0, url.lastIndexOf(',')); | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| import * as A from '../prelude/array'; | import * as A from '../prelude/array'; | ||||||
| import * as S from '../prelude/string'; | import * as S from '../prelude/string'; | ||||||
| import { MfmForest, MfmTree } from './types'; | import { MfmForest, MfmTree } from './prelude'; | ||||||
| import { createTree, createLeaf } from '../prelude/tree'; | import { createTree, createLeaf } from '../prelude/tree'; | ||||||
| 
 | 
 | ||||||
| function isEmptyTextTree(t: MfmTree): boolean { | function isEmptyTextTree(t: MfmTree): boolean { | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| import { mfmLanguage } from './language'; | import { mfmLanguage } from './language'; | ||||||
| import { MfmForest } from './types'; | import { MfmForest } from './prelude'; | ||||||
| import { normalize } from './normalize'; | import { normalize } from './normalize'; | ||||||
| 
 | 
 | ||||||
| export function parse(source: string): MfmForest { | export function parse(source: string): MfmForest { | ||||||
|  |  | ||||||
|  | @ -35,3 +35,5 @@ export function createLeaf(type: string, props: any): MfmTree { | ||||||
| export function createTree(type: string, children: MfmForest, props: any): MfmTree { | export function createTree(type: string, children: MfmForest, props: any): MfmTree { | ||||||
| 	return T.createTree({ type, props }, children); | 	return T.createTree({ type, props }, children); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | export const urlRegex = /^https?:\/\/[\w\/:%#@\$&\?!\(\)\[\]~\.,=\+\-]+/; | ||||||
|  | @ -2,7 +2,7 @@ import { JSDOM } from 'jsdom'; | ||||||
| import config from '../config'; | import config from '../config'; | ||||||
| import { INote } from '../models/note'; | import { INote } from '../models/note'; | ||||||
| import { intersperse } from '../prelude/array'; | import { intersperse } from '../prelude/array'; | ||||||
| import { MfmForest, MfmTree } from './types'; | import { MfmForest, MfmTree } from './prelude'; | ||||||
| 
 | 
 | ||||||
| export function toHtml(tokens: MfmForest, mentionedRemoteUsers: INote['mentionedRemoteUsers'] = []) { | export function toHtml(tokens: MfmForest, mentionedRemoteUsers: INote['mentionedRemoteUsers'] = []) { | ||||||
| 	if (tokens == null) { | 	if (tokens == null) { | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| import { EmojiNode, MfmForest } from '../mfm/types'; | import { EmojiNode, MfmForest } from '../mfm/prelude'; | ||||||
| import { preorderF } from '../prelude/tree'; | import { preorderF } from '../prelude/tree'; | ||||||
| import { unique } from '../prelude/array'; | import { unique } from '../prelude/array'; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| import { HashtagNode, MfmForest } from '../mfm/types'; | import { HashtagNode, MfmForest } from '../mfm/prelude'; | ||||||
| import { preorderF } from '../prelude/tree'; | import { preorderF } from '../prelude/tree'; | ||||||
| import { unique } from '../prelude/array'; | import { unique } from '../prelude/array'; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| // test is located in test/extract-mentions
 | // test is located in test/extract-mentions
 | ||||||
| 
 | 
 | ||||||
| import { MentionNode, MfmForest } from '../mfm/types'; | import { MentionNode, MfmForest } from '../mfm/prelude'; | ||||||
| import { preorderF } from '../prelude/tree'; | import { preorderF } from '../prelude/tree'; | ||||||
| 
 | 
 | ||||||
| export default function(mfmForest: MfmForest): MentionNode['props'][] { | export default function(mfmForest: MfmForest): MentionNode['props'][] { | ||||||
|  |  | ||||||
							
								
								
									
										16
									
								
								test/mfm.ts
									
										
									
									
									
								
							
							
						
						
									
										16
									
								
								test/mfm.ts
									
										
									
									
									
								
							|  | @ -12,7 +12,7 @@ import * as assert from 'assert'; | ||||||
| 
 | 
 | ||||||
| import { parse, parsePlain } from '../src/mfm/parse'; | import { parse, parsePlain } from '../src/mfm/parse'; | ||||||
| import { toHtml } from '../src/mfm/toHtml'; | import { toHtml } from '../src/mfm/toHtml'; | ||||||
| import { createTree as tree, createLeaf as leaf, MfmTree } from '../src/mfm/types'; | import { createTree as tree, createLeaf as leaf, MfmTree } from '../src/mfm/prelude'; | ||||||
| import { removeOrphanedBrackets } from '../src/mfm/language'; | import { removeOrphanedBrackets } from '../src/mfm/language'; | ||||||
| 
 | 
 | ||||||
| function text(text: string): MfmTree { | function text(text: string): MfmTree { | ||||||
|  | @ -840,6 +840,20 @@ describe('MFM', () => { | ||||||
| 					text(')') | 					text(')') | ||||||
| 				]); | 				]); | ||||||
| 			}); | 			}); | ||||||
|  | 
 | ||||||
|  | 			it('ignore non-ascii characters contained url without angle brackets', () => { | ||||||
|  | 				const tokens = parse('https://大石泉すき.example.com'); | ||||||
|  | 				assert.deepStrictEqual(tokens, [ | ||||||
|  | 					text('https://大石泉すき.example.com') | ||||||
|  | 				]); | ||||||
|  | 			}); | ||||||
|  | 
 | ||||||
|  | 			it('match non-ascii characters contained url with angle brackets', () => { | ||||||
|  | 				const tokens = parse('<https://大石泉すき.example.com>'); | ||||||
|  | 				assert.deepStrictEqual(tokens, [ | ||||||
|  | 					leaf('url', { url: 'https://大石泉すき.example.com' }) | ||||||
|  | 				]); | ||||||
|  | 			}); | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		describe('link', () => { | 		describe('link', () => { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue