1diff --git a/README.md b/README.md
2index f6e202a381dc90562512cd33264c39478b7b90da..3f1a12ee7d50c2efec880a6f0597ce4053524268 100644
3--- a/README.md
4+++ b/README.md
5@@ -25,7 +25,6 @@ ### TODO
6
7 - Impove diff display
8 - Add log pagination
9-- Fix submodule link on tree view
10
11 ### Milestone
12 - Mail patch / mailing list
13diff --git a/templates/gititemtree.qtpl b/templates/gititemtree.qtpl
14index 5898506af0e41201754aec08dcb72171efd2a675..2753e24fd356cfec2fd3a412d5d8669266d5da0a 100644
15--- a/templates/gititemtree.qtpl
16+++ b/templates/gititemtree.qtpl
17@@ -1,5 +1,6 @@
18 {% import "git.gabrielgio.me/cerrado/pkg/u" %}
19 {% import "github.com/go-git/go-git/v5/plumbing/object" %}
20+{% import "github.com/go-git/go-git/v5/plumbing/filemode" %}
21
22 {% code type GitItemTreePage struct {
23 Path []string
24@@ -52,10 +53,14 @@ <div class="date"></div>
25 <div class="size"></div>
26 {% endif %}
27 {% for _, e := range g.Tree.Entries %}
28- <div class="mode">{%s Ignore(e.Mode.ToOSFileMode()).String() %}</div>
29 {% if e.Mode.IsFile() %}
30+ <div class="mode">{%s Ignore(e.Mode.ToOSFileMode()).String() %}</div>
31 <div class="name blob"><a href="{%s url(name, Blob, ref, e.Name, g.Path) %}">{%s e.Name %}</a></div>
32+ {% elseif e.Mode == filemode.Submodule %}
33+ <div class="mode">m---------</div>
34+ <div class="name tree">{%s e.Name %} (submodule)</div>
35 {% else %}
36+ <div class="mode">d---------</div>
37 <div class="name tree"><a href="{%s url(name, Folder, ref, e.Name, g.Path) %}">{%s e.Name %}</a></div>
38 {% endif %}
39 <div class="commit"></div>
40diff --git a/templates/gititemtree.qtpl.go b/templates/gititemtree.qtpl.go
41index f8d1fd2880caeda1c09bed34c6cdf9e8193bbec0..f26b5ca558d08f97e2851d3f4a18c18924ed5f77 100644
42--- a/templates/gititemtree.qtpl.go
43+++ b/templates/gititemtree.qtpl.go
44@@ -10,33 +10,36 @@
45 //line gititemtree.qtpl:2
46 import "github.com/go-git/go-git/v5/plumbing/object"
47
48-//line gititemtree.qtpl:4
49+//line gititemtree.qtpl:3
50+import "github.com/go-git/go-git/v5/plumbing/filemode"
51+
52+//line gititemtree.qtpl:5
53 import (
54 qtio422016 "io"
55
56 qt422016 "github.com/valyala/quicktemplate"
57 )
58
59-//line gititemtree.qtpl:4
60+//line gititemtree.qtpl:5
61 var (
62 _ = qtio422016.Copy
63 _ = qt422016.AcquireByteBuffer
64 )
65
66-//line gititemtree.qtpl:4
67+//line gititemtree.qtpl:5
68 type GitItemTreePage struct {
69 Path []string
70 Tree *object.Tree
71 }
72
73-//line gititemtree.qtpl:10
74+//line gititemtree.qtpl:11
75 const (
76 Folder = "tree"
77 Blob = "blob"
78 Root = ""
79 )
80
81-//line gititemtree.qtpl:17
82+//line gititemtree.qtpl:18
83 func url(name, mode, ref, filename string, path []string) string {
84 return u.NewPathing().
85 AddPath(name).
86@@ -47,188 +50,200 @@ AddPath(filename).
87 Done()
88 }
89
90-//line gititemtree.qtpl:28
91+//line gititemtree.qtpl:29
92 func (g *GitItemTreePage) StreamNav(qw422016 *qt422016.Writer, name, ref string) {
93-//line gititemtree.qtpl:28
94+//line gititemtree.qtpl:29
95 StreamGitItemNav(qw422016, name, ref, Tree)
96-//line gititemtree.qtpl:28
97+//line gititemtree.qtpl:29
98 }
99
100-//line gititemtree.qtpl:28
101+//line gititemtree.qtpl:29
102 func (g *GitItemTreePage) WriteNav(qq422016 qtio422016.Writer, name, ref string) {
103-//line gititemtree.qtpl:28
104+//line gititemtree.qtpl:29
105 qw422016 := qt422016.AcquireWriter(qq422016)
106-//line gititemtree.qtpl:28
107+//line gititemtree.qtpl:29
108 g.StreamNav(qw422016, name, ref)
109-//line gititemtree.qtpl:28
110+//line gititemtree.qtpl:29
111 qt422016.ReleaseWriter(qw422016)
112-//line gititemtree.qtpl:28
113+//line gititemtree.qtpl:29
114 }
115
116-//line gititemtree.qtpl:28
117+//line gititemtree.qtpl:29
118 func (g *GitItemTreePage) Nav(name, ref string) string {
119-//line gititemtree.qtpl:28
120+//line gititemtree.qtpl:29
121 qb422016 := qt422016.AcquireByteBuffer()
122-//line gititemtree.qtpl:28
123+//line gititemtree.qtpl:29
124 g.WriteNav(qb422016, name, ref)
125-//line gititemtree.qtpl:28
126+//line gititemtree.qtpl:29
127 qs422016 := string(qb422016.B)
128-//line gititemtree.qtpl:28
129+//line gititemtree.qtpl:29
130 qt422016.ReleaseByteBuffer(qb422016)
131-//line gititemtree.qtpl:28
132+//line gititemtree.qtpl:29
133 return qs422016
134-//line gititemtree.qtpl:28
135+//line gititemtree.qtpl:29
136 }
137
138-//line gititemtree.qtpl:30
139+//line gititemtree.qtpl:31
140 func (g *GitItemTreePage) StreamGitContent(qw422016 *qt422016.Writer, name, ref string) {
141-//line gititemtree.qtpl:30
142+//line gititemtree.qtpl:31
143 qw422016.N().S(`
144 <div class="pathing">
145 `)
146-//line gititemtree.qtpl:33
147+//line gititemtree.qtpl:34
148 if len(g.Path) != 0 {
149-//line gititemtree.qtpl:33
150- qw422016.N().S(`<a href="`)
151 //line gititemtree.qtpl:34
152+ qw422016.N().S(`<a href="`)
153+//line gititemtree.qtpl:35
154 qw422016.E().S(url(name, Folder, ref, Root, []string{}))
155-//line gititemtree.qtpl:34
156+//line gititemtree.qtpl:35
157 qw422016.N().S(`">root/</a>`)
158-//line gititemtree.qtpl:35
159+//line gititemtree.qtpl:36
160 for i, e := range g.Path[:len(g.Path)-1] {
161-//line gititemtree.qtpl:35
162+//line gititemtree.qtpl:36
163 qw422016.N().S(`<a href="`)
164-//line gititemtree.qtpl:36
165+//line gititemtree.qtpl:37
166 qw422016.E().S(url(name, Folder, ref, Root, g.Path[:1+i]))
167-//line gititemtree.qtpl:36
168+//line gititemtree.qtpl:37
169 qw422016.N().S(`">`)
170-//line gititemtree.qtpl:36
171+//line gititemtree.qtpl:37
172 qw422016.E().S(e)
173-//line gititemtree.qtpl:36
174+//line gititemtree.qtpl:37
175 qw422016.N().S(`/</a>`)
176-//line gititemtree.qtpl:37
177+//line gititemtree.qtpl:38
178 }
179-//line gititemtree.qtpl:37
180- qw422016.N().S(`<a>`)
181 //line gititemtree.qtpl:38
182+ qw422016.N().S(`<a>`)
183+//line gititemtree.qtpl:39
184 qw422016.E().S(u.LastOrZero(g.Path))
185-//line gititemtree.qtpl:38
186+//line gititemtree.qtpl:39
187 qw422016.N().S(`</a>`)
188-//line gititemtree.qtpl:39
189+//line gititemtree.qtpl:40
190 } else {
191-//line gititemtree.qtpl:39
192+//line gititemtree.qtpl:40
193 qw422016.N().S(`<a>root/</a>`)
194-//line gititemtree.qtpl:41
195- }
196 //line gititemtree.qtpl:42
197+ }
198+//line gititemtree.qtpl:43
199 qw422016.N().S(`
200 </div>
201 <div class="row">
202 <div class="col-md-12">
203 <div class="tree-list">
204 `)
205-//line gititemtree.qtpl:47
206+//line gititemtree.qtpl:48
207 if len(g.Path) != 0 {
208-//line gititemtree.qtpl:47
209+//line gititemtree.qtpl:48
210 qw422016.N().S(`
211 <div class="mode"><a href="`)
212-//line gititemtree.qtpl:48
213+//line gititemtree.qtpl:49
214 qw422016.E().S(url(name, Folder, ref, g.Path[len(g.Path)-1], g.Path[:len(g.Path)-1]))
215-//line gititemtree.qtpl:48
216+//line gititemtree.qtpl:49
217 qw422016.N().S(`">..</a></div>
218 <div class="name tree"></div>
219 <div class="commit"></div>
220 <div class="date"></div>
221 <div class="size"></div>
222 `)
223-//line gititemtree.qtpl:53
224+//line gititemtree.qtpl:54
225 }
226-//line gititemtree.qtpl:53
227+//line gititemtree.qtpl:54
228 qw422016.N().S(`
229 `)
230-//line gititemtree.qtpl:54
231+//line gititemtree.qtpl:55
232 for _, e := range g.Tree.Entries {
233-//line gititemtree.qtpl:54
234- qw422016.N().S(`
235- <div class="mode">`)
236 //line gititemtree.qtpl:55
237- qw422016.E().S(Ignore(e.Mode.ToOSFileMode()).String())
238-//line gititemtree.qtpl:55
239- qw422016.N().S(`</div>
240+ qw422016.N().S(`
241 `)
242 //line gititemtree.qtpl:56
243 if e.Mode.IsFile() {
244 //line gititemtree.qtpl:56
245 qw422016.N().S(`
246+ <div class="mode">`)
247+//line gititemtree.qtpl:57
248+ qw422016.E().S(Ignore(e.Mode.ToOSFileMode()).String())
249+//line gititemtree.qtpl:57
250+ qw422016.N().S(`</div>
251 <div class="name blob"><a href="`)
252-//line gititemtree.qtpl:57
253+//line gititemtree.qtpl:58
254 qw422016.E().S(url(name, Blob, ref, e.Name, g.Path))
255-//line gititemtree.qtpl:57
256+//line gititemtree.qtpl:58
257 qw422016.N().S(`">`)
258-//line gititemtree.qtpl:57
259+//line gititemtree.qtpl:58
260 qw422016.E().S(e.Name)
261-//line gititemtree.qtpl:57
262+//line gititemtree.qtpl:58
263 qw422016.N().S(`</a></div>
264 `)
265-//line gititemtree.qtpl:58
266+//line gititemtree.qtpl:59
267+ } else if e.Mode == filemode.Submodule {
268+//line gititemtree.qtpl:59
269+ qw422016.N().S(`
270+ <div class="mode">m---------</div>
271+ <div class="name tree">`)
272+//line gititemtree.qtpl:61
273+ qw422016.E().S(e.Name)
274+//line gititemtree.qtpl:61
275+ qw422016.N().S(` (submodule)</div>
276+ `)
277+//line gititemtree.qtpl:62
278 } else {
279-//line gititemtree.qtpl:58
280+//line gititemtree.qtpl:62
281 qw422016.N().S(`
282+ <div class="mode">d---------</div>
283 <div class="name tree"><a href="`)
284-//line gititemtree.qtpl:59
285+//line gititemtree.qtpl:64
286 qw422016.E().S(url(name, Folder, ref, e.Name, g.Path))
287-//line gititemtree.qtpl:59
288+//line gititemtree.qtpl:64
289 qw422016.N().S(`">`)
290-//line gititemtree.qtpl:59
291+//line gititemtree.qtpl:64
292 qw422016.E().S(e.Name)
293-//line gititemtree.qtpl:59
294+//line gititemtree.qtpl:64
295 qw422016.N().S(`</a></div>
296 `)
297-//line gititemtree.qtpl:60
298+//line gititemtree.qtpl:65
299 }
300-//line gititemtree.qtpl:60
301+//line gititemtree.qtpl:65
302 qw422016.N().S(`
303 <div class="commit"></div>
304 <div class="date"></div>
305 <div class="size">`)
306-//line gititemtree.qtpl:63
307+//line gititemtree.qtpl:68
308 qw422016.N().DL(Ignore(g.Tree.Size(e.Name)))
309-//line gititemtree.qtpl:63
310+//line gititemtree.qtpl:68
311 qw422016.N().S(` KiB</div>
312 `)
313-//line gititemtree.qtpl:64
314+//line gititemtree.qtpl:69
315 }
316-//line gititemtree.qtpl:64
317+//line gititemtree.qtpl:69
318 qw422016.N().S(`
319 </div>
320 </div>
321 </div>
322 `)
323-//line gititemtree.qtpl:68
324+//line gititemtree.qtpl:73
325 }
326
327-//line gititemtree.qtpl:68
328+//line gititemtree.qtpl:73
329 func (g *GitItemTreePage) WriteGitContent(qq422016 qtio422016.Writer, name, ref string) {
330-//line gititemtree.qtpl:68
331+//line gititemtree.qtpl:73
332 qw422016 := qt422016.AcquireWriter(qq422016)
333-//line gititemtree.qtpl:68
334+//line gititemtree.qtpl:73
335 g.StreamGitContent(qw422016, name, ref)
336-//line gititemtree.qtpl:68
337+//line gititemtree.qtpl:73
338 qt422016.ReleaseWriter(qw422016)
339-//line gititemtree.qtpl:68
340+//line gititemtree.qtpl:73
341 }
342
343-//line gititemtree.qtpl:68
344+//line gititemtree.qtpl:73
345 func (g *GitItemTreePage) GitContent(name, ref string) string {
346-//line gititemtree.qtpl:68
347+//line gititemtree.qtpl:73
348 qb422016 := qt422016.AcquireByteBuffer()
349-//line gititemtree.qtpl:68
350+//line gititemtree.qtpl:73
351 g.WriteGitContent(qb422016, name, ref)
352-//line gititemtree.qtpl:68
353+//line gititemtree.qtpl:73
354 qs422016 := string(qb422016.B)
355-//line gititemtree.qtpl:68
356+//line gititemtree.qtpl:73
357 qt422016.ReleaseByteBuffer(qb422016)
358-//line gititemtree.qtpl:68
359+//line gititemtree.qtpl:73
360 return qs422016
361-//line gititemtree.qtpl:68
362+//line gititemtree.qtpl:73
363 }