diff --git a/modules/markdown/markdown.go b/modules/markdown/markdown.go
index 813fabe178..183804a9ee 100644
--- a/modules/markdown/markdown.go
+++ b/modules/markdown/markdown.go
@@ -19,6 +19,7 @@ import (
 	"golang.org/x/net/html"
 
 	"code.gitea.io/gitea/modules/base"
+	"code.gitea.io/gitea/modules/markup"
 	"code.gitea.io/gitea/modules/setting"
 )
 
@@ -40,18 +41,6 @@ func IsMarkdownFile(name string) bool {
 	return false
 }
 
-// IsReadmeFile reports whether name looks like a README file
-// based on its name.
-func IsReadmeFile(name string) bool {
-	name = strings.ToLower(name)
-	if len(name) < 6 {
-		return false
-	} else if len(name) == 6 {
-		return name == "readme"
-	}
-	return name[:7] == "readme."
-}
-
 var (
 	// MentionPattern matches string that mentions someone, e.g. @Unknwon
 	MentionPattern = regexp.MustCompile(`(\s|^|\W)@[0-9a-zA-Z-_\.]+`)
@@ -707,3 +696,31 @@ func RenderString(raw, urlPrefix string, metas map[string]string) string {
 func RenderWiki(rawBytes []byte, urlPrefix string, metas map[string]string) string {
 	return string(render(rawBytes, urlPrefix, metas, true))
 }
+
+var (
+	// MarkupName describes markup's name
+	MarkupName = "markdown"
+)
+
+func init() {
+	markup.RegisterParser(Parser{})
+}
+
+// Parser implements markup.Parser
+type Parser struct {
+}
+
+// Name implements markup.Parser
+func (Parser) Name() string {
+	return MarkupName
+}
+
+// Extensions implements markup.Parser
+func (Parser) Extensions() []string {
+	return setting.Markdown.FileExtensions
+}
+
+// Render implements markup.Parser
+func (Parser) Render(rawBytes []byte, urlPrefix string, metas map[string]string, isWiki bool) []byte {
+	return render(rawBytes, urlPrefix, metas, isWiki)
+}
diff --git a/modules/markdown/markdown_test.go b/modules/markdown/markdown_test.go
index 88d289aa0e..8364146573 100644
--- a/modules/markdown/markdown_test.go
+++ b/modules/markdown/markdown_test.go
@@ -1,3 +1,7 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
 package markdown_test
 
 import (
@@ -586,31 +590,6 @@ func TestMisc_IsMarkdownFile(t *testing.T) {
 	}
 }
 
-func TestMisc_IsReadmeFile(t *testing.T) {
-	trueTestCases := []string{
-		"readme",
-		"README",
-		"readME.mdown",
-		"README.md",
-	}
-	falseTestCases := []string{
-		"test.md",
-		"wow.MARKDOWN",
-		"LOL.mDoWn",
-		"test",
-		"abcdefg",
-		"abcdefghijklmnopqrstuvwxyz",
-		"test.md.test",
-	}
-
-	for _, testCase := range trueTestCases {
-		assert.True(t, IsReadmeFile(testCase))
-	}
-	for _, testCase := range falseTestCases {
-		assert.False(t, IsReadmeFile(testCase))
-	}
-}
-
 func TestMisc_IsSameDomain(t *testing.T) {
 	setting.AppURL = AppURL
 	setting.AppSubURL = AppSubURL
diff --git a/modules/markup/markup.go b/modules/markup/markup.go
new file mode 100644
index 0000000000..0847fcfb0d
--- /dev/null
+++ b/modules/markup/markup.go
@@ -0,0 +1,72 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package markup
+
+import (
+	"path/filepath"
+	"strings"
+)
+
+// Parser defines an interface for parsering markup file to HTML
+type Parser interface {
+	Name() string // markup format name
+	Extensions() []string
+	Render(rawBytes []byte, urlPrefix string, metas map[string]string, isWiki bool) []byte
+}
+
+var (
+	parsers = make(map[string]Parser)
+)
+
+// RegisterParser registers a new markup file parser
+func RegisterParser(parser Parser) {
+	for _, ext := range parser.Extensions() {
+		parsers[strings.ToLower(ext)] = parser
+	}
+}
+
+// Render renders markup file to HTML with all specific handling stuff.
+func Render(filename string, rawBytes []byte, urlPrefix string, metas map[string]string) []byte {
+	return render(filename, rawBytes, urlPrefix, metas, false)
+}
+
+func render(filename string, rawBytes []byte, urlPrefix string, metas map[string]string, isWiki bool) []byte {
+	extension := strings.ToLower(filepath.Ext(filename))
+	if parser, ok := parsers[extension]; ok {
+		return parser.Render(rawBytes, urlPrefix, metas, isWiki)
+	}
+	return nil
+}
+
+// RenderString renders Markdown to HTML with special links and returns string type.
+func RenderString(filename string, raw, urlPrefix string, metas map[string]string) string {
+	return string(render(filename, []byte(raw), urlPrefix, metas, false))
+}
+
+// RenderWiki renders markdown wiki page to HTML and return HTML string
+func RenderWiki(filename string, rawBytes []byte, urlPrefix string, metas map[string]string) string {
+	return string(render(filename, rawBytes, urlPrefix, metas, true))
+}
+
+// Type returns if markup format via the filename
+func Type(filename string) string {
+	extension := strings.ToLower(filepath.Ext(filename))
+	if parser, ok := parsers[extension]; ok {
+		return parser.Name()
+	}
+	return ""
+}
+
+// IsReadmeFile reports whether name looks like a README file
+// based on its name.
+func IsReadmeFile(name string) bool {
+	name = strings.ToLower(name)
+	if len(name) < 6 {
+		return false
+	} else if len(name) == 6 {
+		return name == "readme"
+	}
+	return name[:7] == "readme."
+}
diff --git a/modules/markup/markup_test.go b/modules/markup/markup_test.go
new file mode 100644
index 0000000000..92caa8ff96
--- /dev/null
+++ b/modules/markup/markup_test.go
@@ -0,0 +1,36 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package markup
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestMisc_IsReadmeFile(t *testing.T) {
+	trueTestCases := []string{
+		"readme",
+		"README",
+		"readME.mdown",
+		"README.md",
+	}
+	falseTestCases := []string{
+		"test.md",
+		"wow.MARKDOWN",
+		"LOL.mDoWn",
+		"test",
+		"abcdefg",
+		"abcdefghijklmnopqrstuvwxyz",
+		"test.md.test",
+	}
+
+	for _, testCase := range trueTestCases {
+		assert.True(t, IsReadmeFile(testCase))
+	}
+	for _, testCase := range falseTestCases {
+		assert.False(t, IsReadmeFile(testCase))
+	}
+}
diff --git a/routers/repo/view.go b/routers/repo/view.go
index 51443a9452..2e4b2644cd 100644
--- a/routers/repo/view.go
+++ b/routers/repo/view.go
@@ -21,7 +21,7 @@ import (
 	"code.gitea.io/gitea/modules/highlight"
 	"code.gitea.io/gitea/modules/lfs"
 	"code.gitea.io/gitea/modules/log"
-	"code.gitea.io/gitea/modules/markdown"
+	"code.gitea.io/gitea/modules/markup"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/templates"
 	"github.com/Unknwon/paginater"
@@ -56,7 +56,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
 
 	var readmeFile *git.Blob
 	for _, entry := range entries {
-		if entry.IsDir() || !markdown.IsReadmeFile(entry.Name()) {
+		if entry.IsDir() || !markup.IsReadmeFile(entry.Name()) {
 			continue
 		}
 
@@ -87,17 +87,16 @@ func renderDirectory(ctx *context.Context, treeLink string) {
 		if isTextFile {
 			d, _ := ioutil.ReadAll(dataRc)
 			buf = append(buf, d...)
-			switch {
-			case markdown.IsMarkdownFile(readmeFile.Name()):
+			newbuf := markup.Render(readmeFile.Name(), buf, treeLink, ctx.Repo.Repository.ComposeMetas())
+			if newbuf != nil {
 				ctx.Data["IsMarkdown"] = true
-				buf = markdown.Render(buf, treeLink, ctx.Repo.Repository.ComposeMetas())
-			default:
+			} else {
 				// FIXME This is the only way to show non-markdown files
 				// instead of a broken "View Raw" link
 				ctx.Data["IsMarkdown"] = true
-				buf = bytes.Replace(buf, []byte("\n"), []byte(`<br>`), -1)
+				newbuf = bytes.Replace(buf, []byte("\n"), []byte(`<br>`), -1)
 			}
-			ctx.Data["FileContent"] = string(buf)
+			ctx.Data["FileContent"] = string(newbuf)
 		}
 	}
 
@@ -182,13 +181,15 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
 		d, _ := ioutil.ReadAll(dataRc)
 		buf = append(buf, d...)
 
-		isMarkdown := markdown.IsMarkdownFile(blob.Name())
-		ctx.Data["IsMarkdown"] = isMarkdown
+		tp := markup.Type(blob.Name())
+		isSupportedMarkup := tp != ""
+		// FIXME: currently set IsMarkdown for compitable
+		ctx.Data["IsMarkdown"] = isSupportedMarkup
 
-		readmeExist := isMarkdown || markdown.IsReadmeFile(blob.Name())
+		readmeExist := isSupportedMarkup || markup.IsReadmeFile(blob.Name())
 		ctx.Data["ReadmeExist"] = readmeExist
-		if readmeExist && isMarkdown {
-			ctx.Data["FileContent"] = string(markdown.Render(buf, path.Dir(treeLink), ctx.Repo.Repository.ComposeMetas()))
+		if readmeExist && isSupportedMarkup {
+			ctx.Data["FileContent"] = string(markup.Render(blob.Name(), buf, path.Dir(treeLink), ctx.Repo.Repository.ComposeMetas()))
 		} else {
 			// Building code view blocks with line number on server side.
 			var fileContent string
diff --git a/routers/repo/wiki.go b/routers/repo/wiki.go
index c1c05d305a..2dcf37cc86 100644
--- a/routers/repo/wiki.go
+++ b/routers/repo/wiki.go
@@ -19,6 +19,7 @@ import (
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/markdown"
+	"code.gitea.io/gitea/modules/markup"
 )
 
 const (
@@ -322,7 +323,7 @@ func Wiki(ctx *context.Context) {
 	}
 
 	ename := entry.Name()
-	if !markdown.IsMarkdownFile(ename) {
+	if markup.Type(ename) != markdown.MarkupName {
 		ext := strings.ToUpper(filepath.Ext(ename))
 		ctx.Data["FormatWarning"] = fmt.Sprintf("%s rendering is not supported at the moment. Rendered as Markdown.", ext)
 	}