288 lines
6.8 KiB
D
288 lines
6.8 KiB
D
// Copyright Brian Schott (Sir Alaran) 2012.
|
|
// Distributed under the Boost Software License, Version 1.0.
|
|
// (See accompanying file LICENSE_1_0.txt or copy at
|
|
// http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
module main;
|
|
|
|
|
|
import std.algorithm;
|
|
import std.array;
|
|
import std.conv;
|
|
import std.file;
|
|
import std.getopt;
|
|
import std.parallelism;
|
|
import std.path;
|
|
import std.regex;
|
|
import std.stdio;
|
|
import std.range;
|
|
import std.d.lexer;
|
|
|
|
import autocomplete;
|
|
import highlighter;
|
|
import langutils;
|
|
import location;
|
|
import parser;
|
|
|
|
import types;
|
|
import circularbuffer;
|
|
|
|
immutable size_t CIRC_BUFF_SIZE = 4;
|
|
|
|
pure nothrow bool isLineOfCode(TokenType t)
|
|
{
|
|
switch(t)
|
|
{
|
|
case TokenType.Semicolon:
|
|
case TokenType.While:
|
|
case TokenType.If:
|
|
case TokenType.For:
|
|
case TokenType.Foreach:
|
|
case TokenType.Foreach_reverse:
|
|
case TokenType.Case:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loads any import directories specified in /etc/dmd.conf.
|
|
* Bugs: Only works on Linux
|
|
* Returns: the paths specified as -I options in /etc/dmd.conf
|
|
*/
|
|
string[] loadDefaultImports()
|
|
{
|
|
version(linux)
|
|
{
|
|
string path = "/etc/dmd.conf";
|
|
if (!exists(path))
|
|
return [];
|
|
string[] rVal;
|
|
auto file = File(path, "r");
|
|
foreach(char[] line; file.byLine())
|
|
{
|
|
if (!line.startsWith("DFLAGS"))
|
|
continue;
|
|
while ((line = line.find("-I")).length > 0)
|
|
{
|
|
auto end = std.string.indexOf(line, " ");
|
|
auto importDir = line[2 .. end].idup;
|
|
rVal ~= importDir;
|
|
line = line[end .. $];
|
|
}
|
|
}
|
|
return rVal;
|
|
}
|
|
else
|
|
{
|
|
return [];
|
|
}
|
|
}
|
|
|
|
|
|
string[] loadConfig()
|
|
{
|
|
string path = expandTilde("~" ~ dirSeparator ~ ".dscanner");
|
|
string[] dirs;
|
|
if (exists(path))
|
|
{
|
|
auto f = File(path, "r");
|
|
scope(exit) f.close();
|
|
|
|
auto trimRegex = ctRegex!(`\s*$`);
|
|
foreach(string line; lines(f))
|
|
{
|
|
dirs ~= replace(line, trimRegex, "");
|
|
}
|
|
}
|
|
foreach(string importDir; loadDefaultImports()) {
|
|
dirs ~= importDir;
|
|
}
|
|
return dirs;
|
|
}
|
|
|
|
int main(string[] args)
|
|
{
|
|
string[] importDirs;
|
|
bool sloc;
|
|
/+bool dotComplete;+/
|
|
bool json;
|
|
/+bool parenComplete;+/
|
|
bool highlight;
|
|
bool ctags;
|
|
bool recursiveCtags;
|
|
bool format;
|
|
bool help;
|
|
|
|
try
|
|
{
|
|
getopt(args, "I", &importDirs,/+ "dotComplete", &dotComplete,+/ "sloc", &sloc,
|
|
"json", &json, /+"parenComplete", &parenComplete,+/ "highlight", &highlight,
|
|
"ctags", &ctags, "recursive|r|R", &recursiveCtags, "help|h", &help);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
stderr.writeln(e.msg);
|
|
}
|
|
|
|
if (help || (!sloc && /+!dotComplete &&+/ !json /+&& !parenComplete+/ && !highlight
|
|
&& !ctags && !format))
|
|
{
|
|
printHelp();
|
|
return 0;
|
|
}
|
|
|
|
importDirs ~= loadConfig();
|
|
|
|
if (sloc)
|
|
{
|
|
if (args.length == 1)
|
|
{
|
|
writeln(stdin.byLine(KeepTerminator.yes).join().byToken().count!(a => isLineOfCode(a.type))());
|
|
}
|
|
else
|
|
{
|
|
writeln(args[1..$].map!(a => File(a).byLine(KeepTerminator.yes).join().byToken(a))()
|
|
.joiner().count!(a => isLineOfCode(a.type))());
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (highlight)
|
|
{
|
|
File f = args.length == 1 ? stdin : File(args[1]);
|
|
highlighter.highlight(f.byLine(KeepTerminator.yes).join().byToken(
|
|
"", IterationStyle.Everything, TokenStyle.Source));
|
|
return 0;
|
|
}
|
|
|
|
/+if (dotComplete || parenComplete)
|
|
{
|
|
if (isAbsolute(args[1]))
|
|
importDirs ~= dirName(args[1]);
|
|
else
|
|
importDirs ~= getcwd();
|
|
Token[] tokens;
|
|
try
|
|
{
|
|
to!size_t(args[1]);
|
|
auto f = appender!string();
|
|
char[] buf;
|
|
while (stdin.readln(buf))
|
|
f.put(buf);
|
|
tokens = f.data.byToken().array();
|
|
}
|
|
catch(ConvException e)
|
|
{
|
|
tokens = args[1].readText().byToken().array();
|
|
args.popFront();
|
|
}
|
|
auto mod = parseModule(tokens);
|
|
CompletionContext context = new CompletionContext(mod);
|
|
context.importDirectories = importDirs;
|
|
foreach (im; parallel(mod.imports))
|
|
{
|
|
auto p = findAbsPath(importDirs, im);
|
|
if (p is null || !p.exists())
|
|
continue;
|
|
context.addModule(p.readText().byToken().array().parseModule());
|
|
}
|
|
auto complete = AutoComplete(tokens, context);
|
|
if (parenComplete)
|
|
writeln(complete.parenComplete(to!size_t(args[1])));
|
|
else if (dotComplete)
|
|
writeln(complete.dotComplete(to!size_t(args[1])));
|
|
return 0;
|
|
}+/
|
|
|
|
if (json)
|
|
{
|
|
CircularBuffer!(Token) tokens;
|
|
File f = args.length == 1 ? stdin : File(args[1]);
|
|
tokens = new CircularBuffer!(Token)(CIRC_BUFF_SIZE,
|
|
f.byLine(KeepTerminator.yes).join().byToken!(char[])());
|
|
auto mod = parseModule(tokens);
|
|
mod.writeJSONTo(stdout);
|
|
return 0;
|
|
}
|
|
|
|
// if (ctags)
|
|
// {
|
|
// if (!recursiveCtags)
|
|
// {
|
|
// auto tokens = byToken(readText(args[1]));
|
|
// auto mod = parseModule(tokens.array());
|
|
// mod.writeCtagsTo(stdout, args[1]);
|
|
// }
|
|
// else
|
|
// {
|
|
// Module m;
|
|
// foreach (dirEntry; dirEntries(args[1], SpanMode.breadth))
|
|
// {
|
|
// if (!dirEntry.name.endsWith(".d", ".di"))
|
|
// continue;
|
|
// stderr.writeln("Generating tags for ", dirEntry.name);
|
|
// auto tokens = byToken(readText(dirEntry.name));
|
|
// if (m is null)
|
|
// m = parseModule(tokens.array());
|
|
// else
|
|
// {
|
|
// auto mod = parseModule(tokens.array());
|
|
// m.merge(mod);
|
|
// }
|
|
// }
|
|
// m.writeCtagsTo(stdout, "");
|
|
// }
|
|
// }
|
|
return 0;
|
|
}
|
|
|
|
void printHelp()
|
|
{
|
|
writeln(
|
|
q{
|
|
Usage: dscanner options
|
|
|
|
options:
|
|
--help | -h
|
|
Prints this help message
|
|
|
|
--sloc [sourceFiles]
|
|
count the number of logical lines of code in the given
|
|
source files. If no files are specified, a file is read from stdin.
|
|
|
|
--json [sourceFile]
|
|
Generate a JSON summary of the given source file. If no file is
|
|
specifed, the file is read from stdin.
|
|
|
|
--dotComplete [sourceFile] cursorPosition
|
|
Provide autocompletion for the insertion of the dot operator. The cursor
|
|
position is the character position in the *file*, not the position in
|
|
the line. If no file is specified, the file is read from stdin.
|
|
|
|
--parenComplete [sourceFile] cursorPosition
|
|
Provides a listing of function parameters or pre-defined version
|
|
identifiers at the cursor position. The cursor position is the character
|
|
position in the *file*, not the line. If no file is specified, the
|
|
contents are read from stdin.
|
|
|
|
--highlight [sourceFile] - Syntax-highlight the given source file. The
|
|
resulting HTML will be written to standard output.
|
|
|
|
-I includePath
|
|
Include _includePath_ in the list of paths used to search for imports.
|
|
By default dscanner will search in the current working directory as
|
|
well as any paths specified in /etc/dmd.conf. This is only used for the
|
|
--parenComplete and --dotComplete options.
|
|
|
|
--ctags sourceFile
|
|
Generates ctags information from the given source code file. Note that
|
|
ctags information requires a filename, so stdin cannot be used in place
|
|
of a filename.
|
|
|
|
--recursive | -R | -r directory
|
|
When used with --ctags, dscanner will produce ctags output for all .d
|
|
and .di files contained within directory and its sub-directories.});
|
|
}
|