Skip to content

Commit 047cbd2

Browse files
authored
Merge pull request #23 from JunoLab/sp/fixmodulestuff
Fix module detection (for real this time)
2 parents 9375205 + 3d8c1c2 commit 047cbd2

File tree

3 files changed

+65
-25
lines changed

3 files changed

+65
-25
lines changed

REQUIRE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ julia 0.5-
22
Lazy
33
MacroTools
44
LNR
5+
Tokenize

src/module.jl

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using Tokenize
2+
13
# –––––––––––––––
24
# Some file utils
35
# –––––––––––––––
@@ -50,42 +52,59 @@ end
5052
# ––––––––––––––
5153
# The Good Stuff
5254
# ––––––––––––––
55+
const SCOPE_STARTERS = [Tokens.BEGIN,
56+
Tokens.WHILE,
57+
Tokens.IF,
58+
Tokens.FOR,
59+
Tokens.TRY,
60+
Tokens.FUNCTION,
61+
Tokens.MACRO,
62+
Tokens.LET,
63+
Tokens.ABSTRACT,
64+
Tokens.TYPE,
65+
Tokens.BITSTYPE,
66+
Tokens.IMMUTABLE,
67+
Tokens.DO,
68+
Tokens.QUOTE]
69+
70+
const MODULE_STARTERS = [Tokens.MODULE, Tokens.BAREMODULE]
5371

5472
"""
5573
Takes Julia source code and a line number, gives back the string name
5674
of the module at that line.
5775
"""
58-
# TODO: do this properly (e.g. by using JuliaParser): `end`-recognition is super
59-
# naive below
6076
function codemodule(code, line)
6177
stack = String[]
62-
# count all unterminated block openers
78+
# count all unterminated block openers and brackets
6379
n_openers = 0
64-
for l in split(code, "\n")[1:line]
65-
# match all new modules and push them to stack
66-
m = match(r"^\s*(?:module|baremodule) ([A-Za-z]+)", l)
67-
if m != nothing
68-
push!(stack, m.captures[1])
69-
continue
70-
end
71-
72-
# match all block openers that aren't modules
73-
if ismatch(r"\b(if|while|for|begin|function|macro|type|immutable|try|let|do|quote)\b(?!.*(\s|;)end\b).*$", l)
74-
n_openers += 1
75-
continue
76-
end
77-
78-
# match all `end`s with only whitespace around them
79-
if ismatch(r"^\s*(?:end)\s*$", l)
80-
# if there are no more open blocks, pop the latest
81-
# added (sub)module from the stack if it isn't empty already
82-
if n_openers == 0
83-
!isempty(stack) && pop!(stack)
84-
else
85-
n_openers -= 1
80+
n_brackets = 0
81+
# index of next modulename token
82+
next_modulename = -1
83+
84+
ts = tokenize(code)
85+
86+
for (i, t) in enumerate(ts)
87+
Tokens.startpos(t)[1] > line && break
88+
89+
# ignore everything in square brackets, because of the ambiguity
90+
# with `end` indexing
91+
if Tokens.kind(t) == Tokens.LSQUARE
92+
n_brackets += 1
93+
elseif n_brackets > 0
94+
if Tokens.kind(t) == Tokens.RSQUARE
95+
n_brackets -= 1
8696
end
97+
elseif Tokens.exactkind(t) in MODULE_STARTERS # new module
98+
next_modulename = i + 2
99+
elseif i == next_modulename && Tokens.kind(t) == Tokens.IDENTIFIER
100+
push!(stack, Tokens.untokenize(t))
101+
elseif Tokens.exactkind(t) in SCOPE_STARTERS # new non-module scope
102+
n_openers += 1
103+
elseif Tokens.exactkind(t) == Tokens.END # scope ended
104+
n_openers == 0 ? (!isempty(stack) && pop!(stack)) : n_openers -= 1
87105
end
88106
end
107+
89108
return join(stack, ".")
90109
end
91110

test/runtests.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,23 @@ with multiline docs
4545
@test CodeTools.signature(Binding(Main, :foo)) == nothing
4646
@test CodeTools.completiontype(foo) == "constant"
4747
end
48+
49+
# module detection tests
50+
code = ["""
51+
module Mod1
52+
[x for x=1:2]
53+
end
54+
1+1
55+
""",
56+
"""
57+
module Mod2
58+
module Foo
59+
# for
60+
end
61+
1+1
62+
"""]
63+
64+
@test CodeTools.codemodule(code[1], 2) == "Mod1"
65+
@test CodeTools.codemodule(code[1], 4) == ""
66+
@test CodeTools.codemodule(code[2], 3) == "Mod2.Foo"
67+
@test CodeTools.codemodule(code[2], 5) == "Mod2"

0 commit comments

Comments
 (0)