Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
J
jadx
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
open-source
jadx
Commits
ebf06fde
Commit
ebf06fde
authored
Dec 27, 2013
by
Skylot
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
gui: remove not generated elements from class node tree
parent
438b3b50
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
132 additions
and
53 deletions
+132
-53
JavaClass.java
jadx-core/src/main/java/jadx/api/JavaClass.java
+21
-17
JavaMethod.java
jadx-core/src/main/java/jadx/api/JavaMethod.java
+8
-9
ClassGen.java
jadx-core/src/main/java/jadx/core/codegen/ClassGen.java
+4
-7
InsnGen.java
jadx-core/src/main/java/jadx/core/codegen/InsnGen.java
+1
-0
MethodNode.java
jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java
+2
-3
MainWindow.java
jadx-gui/src/main/java/jadx/gui/MainWindow.java
+21
-0
JClass.java
jadx-gui/src/main/java/jadx/gui/treemodel/JClass.java
+26
-12
JMethod.java
jadx-gui/src/main/java/jadx/gui/treemodel/JMethod.java
+13
-5
TextNode.java
jadx-gui/src/main/java/jadx/gui/treemodel/TextNode.java
+35
-0
Messages_en_US.properties
jadx-gui/src/main/resources/i18n/Messages_en_US.properties
+1
-0
No files found.
jadx-core/src/main/java/jadx/api/JavaClass.java
View file @
ebf06fde
package
jadx
.
api
;
package
jadx
.
api
;
import
jadx.core.codegen.CodeWriter
;
import
jadx.core.codegen.CodeWriter
;
import
jadx.core.dex.attributes.AttributeFlag
;
import
jadx.core.dex.info.AccessInfo
;
import
jadx.core.dex.info.AccessInfo
;
import
jadx.core.dex.nodes.ClassNode
;
import
jadx.core.dex.nodes.ClassNode
;
import
jadx.core.dex.nodes.FieldNode
;
import
jadx.core.dex.nodes.FieldNode
;
...
@@ -15,44 +16,47 @@ public final class JavaClass {
...
@@ -15,44 +16,47 @@ public final class JavaClass {
private
final
Decompiler
decompiler
;
private
final
Decompiler
decompiler
;
private
final
ClassNode
cls
;
private
final
ClassNode
cls
;
private
final
List
<
JavaClass
>
innerClasses
;
private
final
List
<
JavaField
>
fields
;
private
List
<
JavaClass
>
innerClasses
=
Collections
.
emptyList
();
private
final
List
<
JavaMethod
>
methods
;
private
List
<
JavaField
>
fields
=
Collections
.
emptyList
();
private
List
<
JavaMethod
>
methods
=
Collections
.
emptyList
();
JavaClass
(
Decompiler
decompiler
,
ClassNode
classNode
)
{
JavaClass
(
Decompiler
decompiler
,
ClassNode
classNode
)
{
this
.
decompiler
=
decompiler
;
this
.
decompiler
=
decompiler
;
this
.
cls
=
classNode
;
this
.
cls
=
classNode
;
}
public
void
decompile
()
{
decompiler
.
processClass
(
cls
);
int
inClsCount
=
cls
.
getInnerClasses
().
size
();
int
inClsCount
=
cls
.
getInnerClasses
().
size
();
if
(
inClsCount
==
0
)
{
if
(
inClsCount
!=
0
)
{
this
.
innerClasses
=
Collections
.
emptyList
();
}
else
{
List
<
JavaClass
>
list
=
new
ArrayList
<
JavaClass
>(
inClsCount
);
List
<
JavaClass
>
list
=
new
ArrayList
<
JavaClass
>(
inClsCount
);
for
(
ClassNode
inner
:
cls
.
getInnerClasses
())
{
for
(
ClassNode
inner
:
cls
.
getInnerClasses
())
{
if
(!
inner
.
getAttributes
().
contains
(
AttributeFlag
.
DONT_GENERATE
))
{
list
.
add
(
new
JavaClass
(
decompiler
,
inner
));
list
.
add
(
new
JavaClass
(
decompiler
,
inner
));
}
}
}
this
.
innerClasses
=
Collections
.
unmodifiableList
(
list
);
this
.
innerClasses
=
Collections
.
unmodifiableList
(
list
);
}
}
int
fieldsCount
=
cls
.
getFields
().
size
();
int
fieldsCount
=
cls
.
getFields
().
size
();
if
(
fieldsCount
==
0
)
{
if
(
fieldsCount
!=
0
)
{
this
.
fields
=
Collections
.
emptyList
();
}
else
{
List
<
JavaField
>
flds
=
new
ArrayList
<
JavaField
>(
fieldsCount
);
List
<
JavaField
>
flds
=
new
ArrayList
<
JavaField
>(
fieldsCount
);
for
(
FieldNode
f
:
cls
.
getFields
())
{
for
(
FieldNode
f
:
cls
.
getFields
())
{
if
(!
f
.
getAttributes
().
contains
(
AttributeFlag
.
DONT_GENERATE
))
{
flds
.
add
(
new
JavaField
(
f
));
flds
.
add
(
new
JavaField
(
f
));
}
}
}
this
.
fields
=
Collections
.
unmodifiableList
(
flds
);
this
.
fields
=
Collections
.
unmodifiableList
(
flds
);
}
}
int
methodsCount
=
cls
.
getMethods
().
size
();
int
methodsCount
=
cls
.
getMethods
().
size
();
if
(
methodsCount
==
0
)
{
if
(
methodsCount
!=
0
)
{
this
.
methods
=
Collections
.
emptyList
();
}
else
{
List
<
JavaMethod
>
mths
=
new
ArrayList
<
JavaMethod
>(
methodsCount
);
List
<
JavaMethod
>
mths
=
new
ArrayList
<
JavaMethod
>(
methodsCount
);
for
(
MethodNode
m
:
cls
.
getMethods
())
{
for
(
MethodNode
m
:
cls
.
getMethods
())
{
if
(!
m
.
getA
ccessFlags
().
isSynthetic
(
))
{
if
(!
m
.
getA
ttributes
().
contains
(
AttributeFlag
.
DONT_GENERATE
))
{
mths
.
add
(
new
JavaMethod
(
m
));
mths
.
add
(
new
JavaMethod
(
this
,
m
));
}
}
}
}
Collections
.
sort
(
mths
,
new
Comparator
<
JavaMethod
>()
{
Collections
.
sort
(
mths
,
new
Comparator
<
JavaMethod
>()
{
...
@@ -68,7 +72,7 @@ public final class JavaClass {
...
@@ -68,7 +72,7 @@ public final class JavaClass {
public
String
getCode
()
{
public
String
getCode
()
{
CodeWriter
code
=
cls
.
getCode
();
CodeWriter
code
=
cls
.
getCode
();
if
(
code
==
null
)
{
if
(
code
==
null
)
{
decompile
r
.
processClass
(
cls
);
decompile
(
);
code
=
cls
.
getCode
();
code
=
cls
.
getCode
();
}
}
return
code
!=
null
?
code
.
toString
()
:
"error processing class"
;
return
code
!=
null
?
code
.
toString
()
:
"error processing class"
;
...
...
jadx-core/src/main/java/jadx/api/JavaMethod.java
View file @
ebf06fde
package
jadx
.
api
;
package
jadx
.
api
;
import
jadx.core.dex.info.AccessInfo
;
import
jadx.core.dex.info.AccessInfo
;
import
jadx.core.dex.info.MethodInfo
;
import
jadx.core.dex.instructions.args.ArgType
;
import
jadx.core.dex.instructions.args.ArgType
;
import
jadx.core.dex.nodes.MethodNode
;
import
jadx.core.dex.nodes.MethodNode
;
...
@@ -9,19 +8,19 @@ import java.util.List;
...
@@ -9,19 +8,19 @@ import java.util.List;
public
final
class
JavaMethod
{
public
final
class
JavaMethod
{
private
final
MethodNode
mth
;
private
final
MethodNode
mth
;
private
final
JavaClass
parent
;
public
JavaMethod
(
MethodNode
m
)
{
public
JavaMethod
(
JavaClass
cls
,
MethodNode
m
)
{
this
.
parent
=
cls
;
this
.
mth
=
m
;
this
.
mth
=
m
;
}
}
public
String
getName
()
{
public
String
getName
()
{
MethodInfo
mi
=
mth
.
getMethodInfo
();
return
mth
.
getMethodInfo
().
getName
();
if
(
mi
.
isConstructor
())
{
return
mth
.
getParentClass
().
getShortName
();
}
else
if
(
mi
.
isClassInit
())
{
return
"static"
;
}
}
return
mi
.
getName
();
public
JavaClass
getDeclaringClass
()
{
return
parent
;
}
}
public
AccessInfo
getAccessFlags
()
{
public
AccessInfo
getAccessFlags
()
{
...
...
jadx-core/src/main/java/jadx/core/codegen/ClassGen.java
View file @
ebf06fde
...
@@ -216,9 +216,9 @@ public class ClassGen {
...
@@ -216,9 +216,9 @@ public class ClassGen {
CodeWriter
code
=
new
CodeWriter
(
clsCode
.
getIndent
()
+
1
);
CodeWriter
code
=
new
CodeWriter
(
clsCode
.
getIndent
()
+
1
);
for
(
Iterator
<
MethodNode
>
it
=
mthList
.
iterator
();
it
.
hasNext
();
)
{
for
(
Iterator
<
MethodNode
>
it
=
mthList
.
iterator
();
it
.
hasNext
();
)
{
MethodNode
mth
=
it
.
next
();
MethodNode
mth
=
it
.
next
();
if
(
mth
.
getAttributes
().
contains
(
AttributeFlag
.
DONT_GENERATE
))
if
(
mth
.
getAttributes
().
contains
(
AttributeFlag
.
DONT_GENERATE
))
{
continue
;
continue
;
}
try
{
try
{
if
(
mth
.
getAccessFlags
().
isAbstract
()
||
mth
.
getAccessFlags
().
isNative
())
{
if
(
mth
.
getAccessFlags
().
isAbstract
()
||
mth
.
getAccessFlags
().
isNative
())
{
MethodGen
mthGen
=
new
MethodGen
(
this
,
mth
);
MethodGen
mthGen
=
new
MethodGen
(
this
,
mth
);
...
@@ -232,9 +232,6 @@ public class ClassGen {
...
@@ -232,9 +232,6 @@ public class ClassGen {
}
}
code
.
add
(
';'
);
code
.
add
(
';'
);
}
else
{
}
else
{
if
(
mth
.
isNoCode
())
continue
;
MethodGen
mthGen
=
new
MethodGen
(
this
,
mth
);
MethodGen
mthGen
=
new
MethodGen
(
this
,
mth
);
if
(
mth
.
getAttributes
().
contains
(
AttributeFlag
.
INCONSISTENT_CODE
))
{
if
(
mth
.
getAttributes
().
contains
(
AttributeFlag
.
INCONSISTENT_CODE
))
{
code
.
startLine
(
"/* JADX WARNING: inconsistent code */"
);
code
.
startLine
(
"/* JADX WARNING: inconsistent code */"
);
...
@@ -253,10 +250,10 @@ public class ClassGen {
...
@@ -253,10 +250,10 @@ public class ClassGen {
String
msg
=
ErrorsCounter
.
methodError
(
mth
,
"Method generation error"
,
e
);
String
msg
=
ErrorsCounter
.
methodError
(
mth
,
"Method generation error"
,
e
);
code
.
startLine
(
"/* "
+
msg
+
CodeWriter
.
NL
+
Utils
.
getStackTrace
(
e
)
+
" */"
);
code
.
startLine
(
"/* "
+
msg
+
CodeWriter
.
NL
+
Utils
.
getStackTrace
(
e
)
+
" */"
);
}
}
if
(
it
.
hasNext
())
{
if
(
it
.
hasNext
())
code
.
newLine
();
code
.
newLine
();
}
}
}
return
code
;
return
code
;
}
}
...
...
jadx-core/src/main/java/jadx/core/codegen/InsnGen.java
View file @
ebf06fde
...
@@ -533,6 +533,7 @@ public class InsnGen {
...
@@ -533,6 +533,7 @@ public class InsnGen {
}
else
{
}
else
{
parent
=
cls
.
getSuperClass
();
parent
=
cls
.
getSuperClass
();
}
}
cls
.
getAttributes
().
add
(
AttributeFlag
.
DONT_GENERATE
);
MethodNode
defCtr
=
cls
.
getDefaultConstructor
();
MethodNode
defCtr
=
cls
.
getDefaultConstructor
();
if
(
RegionUtils
.
notEmpty
(
defCtr
.
getRegion
()))
{
if
(
RegionUtils
.
notEmpty
(
defCtr
.
getRegion
()))
{
defCtr
.
getAttributes
().
add
(
AttributeFlag
.
ANONYMOUS_CONSTRUCTOR
);
defCtr
.
getAttributes
().
add
(
AttributeFlag
.
ANONYMOUS_CONSTRUCTOR
);
...
...
jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java
View file @
ebf06fde
...
@@ -124,14 +124,13 @@ public class MethodNode extends LineAttrNode implements ILoadable {
...
@@ -124,14 +124,13 @@ public class MethodNode extends LineAttrNode implements ILoadable {
@Override
@Override
public
void
unload
()
{
public
void
unload
()
{
if
(
noCode
)
if
(
noCode
)
{
return
;
return
;
}
if
(
instructions
!=
null
)
instructions
.
clear
();
if
(
instructions
!=
null
)
instructions
.
clear
();
blocks
=
null
;
blocks
=
null
;
exitBlocks
=
null
;
exitBlocks
=
null
;
if
(
exceptionHandlers
!=
null
)
exceptionHandlers
.
clear
();
if
(
exceptionHandlers
!=
null
)
exceptionHandlers
.
clear
();
getAttributes
().
clear
();
noCode
=
true
;
noCode
=
true
;
}
}
...
...
jadx-gui/src/main/java/jadx/gui/MainWindow.java
View file @
ebf06fde
...
@@ -26,14 +26,18 @@ import javax.swing.JToolBar;
...
@@ -26,14 +26,18 @@ import javax.swing.JToolBar;
import
javax.swing.JTree
;
import
javax.swing.JTree
;
import
javax.swing.KeyStroke
;
import
javax.swing.KeyStroke
;
import
javax.swing.ProgressMonitor
;
import
javax.swing.ProgressMonitor
;
import
javax.swing.event.TreeExpansionEvent
;
import
javax.swing.event.TreeSelectionEvent
;
import
javax.swing.event.TreeSelectionEvent
;
import
javax.swing.event.TreeSelectionListener
;
import
javax.swing.event.TreeSelectionListener
;
import
javax.swing.event.TreeWillExpandListener
;
import
javax.swing.filechooser.FileNameExtensionFilter
;
import
javax.swing.filechooser.FileNameExtensionFilter
;
import
javax.swing.plaf.basic.BasicButtonUI
;
import
javax.swing.plaf.basic.BasicButtonUI
;
import
javax.swing.text.BadLocationException
;
import
javax.swing.text.BadLocationException
;
import
javax.swing.tree.DefaultMutableTreeNode
;
import
javax.swing.tree.DefaultMutableTreeNode
;
import
javax.swing.tree.DefaultTreeCellRenderer
;
import
javax.swing.tree.DefaultTreeCellRenderer
;
import
javax.swing.tree.DefaultTreeModel
;
import
javax.swing.tree.DefaultTreeModel
;
import
javax.swing.tree.ExpandVetoException
;
import
javax.swing.tree.TreePath
;
import
javax.swing.tree.TreeSelectionModel
;
import
javax.swing.tree.TreeSelectionModel
;
import
java.awt.BorderLayout
;
import
java.awt.BorderLayout
;
import
java.awt.Color
;
import
java.awt.Color
;
...
@@ -192,6 +196,7 @@ public class MainWindow extends JFrame {
...
@@ -192,6 +196,7 @@ public class MainWindow extends JFrame {
}
}
private
void
showCode
(
JClass
cls
,
int
line
)
{
private
void
showCode
(
JClass
cls
,
int
line
)
{
cls
.
load
();
JPanel
panel
=
(
JPanel
)
openTabs
.
get
(
cls
);
JPanel
panel
=
(
JPanel
)
openTabs
.
get
(
cls
);
if
(
panel
!=
null
)
{
if
(
panel
!=
null
)
{
panel
=
(
JPanel
)
openTabs
.
get
(
cls
);
panel
=
(
JPanel
)
openTabs
.
get
(
cls
);
...
@@ -379,6 +384,22 @@ public class MainWindow extends JFrame {
...
@@ -379,6 +384,22 @@ public class MainWindow extends JFrame {
return
c
;
return
c
;
}
}
});
});
tree
.
addTreeWillExpandListener
(
new
TreeWillExpandListener
()
{
@Override
public
void
treeWillExpand
(
TreeExpansionEvent
event
)
throws
ExpandVetoException
{
TreePath
path
=
event
.
getPath
();
Object
node
=
path
.
getLastPathComponent
();
if
(
node
instanceof
JClass
)
{
JClass
cls
=
(
JClass
)
node
;
cls
.
load
();
}
}
@Override
public
void
treeWillCollapse
(
TreeExpansionEvent
event
)
throws
ExpandVetoException
{
}
});
JScrollPane
treeScrollPane
=
new
JScrollPane
(
tree
);
JScrollPane
treeScrollPane
=
new
JScrollPane
(
tree
);
splitPane
.
setLeftComponent
(
treeScrollPane
);
splitPane
.
setLeftComponent
(
treeScrollPane
);
...
...
jadx-gui/src/main/java/jadx/gui/treemodel/JClass.java
View file @
ebf06fde
...
@@ -4,6 +4,7 @@ import jadx.api.JavaClass;
...
@@ -4,6 +4,7 @@ import jadx.api.JavaClass;
import
jadx.api.JavaField
;
import
jadx.api.JavaField
;
import
jadx.api.JavaMethod
;
import
jadx.api.JavaMethod
;
import
jadx.core.dex.info.AccessInfo
;
import
jadx.core.dex.info.AccessInfo
;
import
jadx.gui.utils.NLS
;
import
jadx.gui.utils.Utils
;
import
jadx.gui.utils.Utils
;
import
javax.swing.Icon
;
import
javax.swing.Icon
;
...
@@ -22,6 +23,7 @@ public class JClass extends JNode {
...
@@ -22,6 +23,7 @@ public class JClass extends JNode {
private
final
JavaClass
cls
;
private
final
JavaClass
cls
;
private
JClass
jParrent
;
private
JClass
jParrent
;
private
boolean
loaded
;
public
JClass
(
JavaClass
cls
)
{
public
JClass
(
JavaClass
cls
)
{
this
.
cls
=
cls
;
this
.
cls
=
cls
;
...
@@ -31,9 +33,20 @@ public class JClass extends JNode {
...
@@ -31,9 +33,20 @@ public class JClass extends JNode {
return
cls
;
return
cls
;
}
}
public
synchronized
void
load
()
{
if
(!
loaded
)
{
cls
.
decompile
();
loaded
=
true
;
updateChilds
();
}
}
@Override
@Override
public
void
updateChilds
()
{
public
void
updateChilds
()
{
removeAllChildren
();
removeAllChildren
();
if
(!
loaded
)
{
add
(
new
TextNode
(
NLS
.
str
(
"tree.loading"
)));
}
else
{
JClass
currentParent
=
jParrent
==
null
?
this
:
jParrent
;
JClass
currentParent
=
jParrent
==
null
?
this
:
jParrent
;
for
(
JavaClass
javaClass
:
cls
.
getInnerClasses
())
{
for
(
JavaClass
javaClass
:
cls
.
getInnerClasses
())
{
JClass
child
=
new
JClass
(
javaClass
);
JClass
child
=
new
JClass
(
javaClass
);
...
@@ -48,6 +61,7 @@ public class JClass extends JNode {
...
@@ -48,6 +61,7 @@ public class JClass extends JNode {
add
(
new
JMethod
(
m
,
currentParent
));
add
(
new
JMethod
(
m
,
currentParent
));
}
}
}
}
}
public
String
getCode
()
{
public
String
getCode
()
{
return
cls
.
getCode
();
return
cls
.
getCode
();
...
...
jadx-gui/src/main/java/jadx/gui/treemodel/JMethod.java
View file @
ebf06fde
...
@@ -47,8 +47,12 @@ public class JMethod extends JNode {
...
@@ -47,8 +47,12 @@ public class JMethod extends JNode {
public
Icon
getIcon
()
{
public
Icon
getIcon
()
{
AccessInfo
accessFlags
=
mth
.
getAccessFlags
();
AccessInfo
accessFlags
=
mth
.
getAccessFlags
();
OverlayIcon
icon
=
Utils
.
makeIcon
(
accessFlags
,
ICON_MTH_PUB
,
ICON_MTH_PRI
,
ICON_MTH_PRO
,
ICON_MTH_DEF
);
OverlayIcon
icon
=
Utils
.
makeIcon
(
accessFlags
,
ICON_MTH_PUB
,
ICON_MTH_PRI
,
ICON_MTH_PRO
,
ICON_MTH_DEF
);
if
(
accessFlags
.
isConstructor
())
icon
.
add
(
ICON_CONSTRUCTOR
);
if
(
accessFlags
.
isConstructor
())
{
if
(
accessFlags
.
isSynchronized
())
icon
.
add
(
ICON_SYNC
);
icon
.
add
(
ICON_CONSTRUCTOR
);
}
if
(
accessFlags
.
isSynchronized
())
{
icon
.
add
(
ICON_SYNC
);
}
return
icon
;
return
icon
;
}
}
...
@@ -57,15 +61,19 @@ public class JMethod extends JNode {
...
@@ -57,15 +61,19 @@ public class JMethod extends JNode {
if
(
mth
.
isClassInit
())
{
if
(
mth
.
isClassInit
())
{
return
"{...}"
;
return
"{...}"
;
}
}
StringBuilder
base
=
new
StringBuilder
();
StringBuilder
base
=
new
StringBuilder
();
if
(
mth
.
isConstructor
())
{
base
.
append
(
mth
.
getDeclaringClass
().
getShortName
());
}
else
{
base
.
append
(
mth
.
getName
());
base
.
append
(
mth
.
getName
());
}
base
.
append
(
'('
);
base
.
append
(
'('
);
for
(
Iterator
<
ArgType
>
it
=
mth
.
getArguments
().
iterator
();
it
.
hasNext
();
)
{
for
(
Iterator
<
ArgType
>
it
=
mth
.
getArguments
().
iterator
();
it
.
hasNext
();
)
{
base
.
append
(
Utils
.
typeStr
(
it
.
next
()));
base
.
append
(
Utils
.
typeStr
(
it
.
next
()));
if
(
it
.
hasNext
())
if
(
it
.
hasNext
())
{
base
.
append
(
", "
);
base
.
append
(
", "
);
}
}
}
base
.
append
(
')'
);
base
.
append
(
')'
);
return
Utils
.
typeFormat
(
base
.
toString
(),
mth
.
getReturnType
());
return
Utils
.
typeFormat
(
base
.
toString
(),
mth
.
getReturnType
());
}
}
...
...
jadx-gui/src/main/java/jadx/gui/treemodel/TextNode.java
0 → 100644
View file @
ebf06fde
package
jadx
.
gui
.
treemodel
;
import
javax.swing.Icon
;
public
class
TextNode
extends
JNode
{
private
final
String
label
;
public
TextNode
(
String
str
)
{
this
.
label
=
str
;
}
@Override
public
JClass
getJParent
()
{
return
null
;
}
@Override
public
int
getLine
()
{
return
0
;
}
@Override
public
void
updateChilds
()
{
}
@Override
public
Icon
getIcon
()
{
return
null
;
}
@Override
public
String
toString
()
{
return
label
;
}
}
jadx-gui/src/main/resources/i18n/Messages_en_US.properties
View file @
ebf06fde
...
@@ -6,6 +6,7 @@ file.select=Select
...
@@ -6,6 +6,7 @@ file.select=Select
file.exit
=
Exit
file.exit
=
Exit
tree.flatten
=
Flatten packages
tree.flatten
=
Flatten packages
tree.loading
=
Loading...
search
=
Search
search
=
Search
search.previous
=
Previous
search.previous
=
Previous
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment