improved todo list widget and tool

This commit is contained in:
Basile Burg 2015-01-17 03:54:39 +01:00
parent 14565e51b8
commit 2968b95e0a
3 changed files with 90 additions and 33 deletions

View File

@ -18,7 +18,6 @@ object CurrentProject: TCEProject
end end
item item
name = 'release' name = 'release'
outputOptions.inlining = True
outputOptions.boundsCheck = offAlways outputOptions.boundsCheck = offAlways
outputOptions.optimizations = True outputOptions.optimizations = True
outputOptions.release = True outputOptions.release = True
@ -33,7 +32,7 @@ object CurrentProject: TCEProject
Sources.Strings = ( Sources.Strings = (
'cetodo.d' 'cetodo.d'
) )
ConfigurationIndex = 0 ConfigurationIndex = 1
LibraryAliases.Strings = ( LibraryAliases.Strings = (
'libdparse' 'libdparse'
) )

View File

@ -1,32 +1,39 @@
/******************************************************************************* /*******************************************************************************
TODO source code analyzer for Coedit projects/files TODO source code analyzer for Coedit projects/files
Format: // TODO [fields] : text ## Format:
``
// TODO [fields] : text
``
- TODO: used to detect that the comment is a "TODO" comment. The keyword is not - TODO: used to detect that the comment is a "TODO" comment. The keyword is not
case sensitive. case sensitive.
- fields: an optional list of property with a format similar to the execution argument - fields: an optional list of property with a format similar to the execution argument
of a program: -<char x><property for char x>-<char y><property for char y>. of a program: `-<char x><property for char x>-<char y><property for char y>`.
possible fields include: possible fields include:
- c: TODO category, e.g: -cserialization -cpersistence -cerrorhandling - c: TODO category, e.g: _-cserialization_, _-cpersistence_, _ -cerrorhandling_.
- a: TODO assignee, e.g: -aMisterFreeze -aMadameMichou -aJhonSmith - a: TODO assignee, e.g: _-aMisterFreeze_, _-aMadameMichou_, _-aJhonSmith_.
- p: TODO priority, eg: -p8 -p0 - p: TODO priority, eg: _-p8_, _-p0_.
- s: TODO status, e.g -sPartiallyFixed, -sDone - s: TODO status, e.g _-sPartiallyFixed_, _-sDone_.
- text: the literal message, e.g: "set this property as const() to set it read only". - text: the literal message, e.g: "set this property as const() to set it read only".
full examples: ## Examples:
// TODO: set this property as const() to set it read only. ``// TODO: set this property as const() to make it read-only.``
// TODO-cfeature: save this property in the inifile.
// TODO-cannnotations-p8: annotates the member of the module as @safe and if possible nothrow.
- widget to tool IPC: ``// TODO-cfeature: save this property in the inifile.``
The widget call the tool with a list of file as argument and read the process ``// TODO-cannnotations-p8: annotate the members of the module with @safe and if possible nothrow.``
output on exit. the widget expects to find some TODO items in LFM format, according
to the classes declarations of TTodoItems (the collection container) and TTodoItem(the collection item) ## Widget-to-tool IPC:
The widget calls the tool with a file list as argument and reads the process
output on exit. The widget expects to find some _TODO items_ in _LFM_ format,
according to the classes declarations of TTodoItems (the collection container)
and TTodoItem(the collection item).
********************************************************************************/ ********************************************************************************/
module cetodo; module cetodo;
@ -38,18 +45,26 @@ import std.file, std.path, std.range;
// libdparse // libdparse
import std.d.ast, std.d.lexer, std.d.parser; import std.d.ast, std.d.lexer, std.d.parser;
/// Encapsulates the fields of a _TODO comment_.
private struct TodoItem private struct TodoItem
{ {
/**
* Enumerates the possible fields of _a TODO comment_.
* They must match the published member of the widget-side class TTodoItem.
*/
private enum TodoField {filename, line, text, category, assignee, priority, status} private enum TodoField {filename, line, text, category, assignee, priority, status}
private string[TodoField] fFields; private string[TodoField] fFields;
/** /**
* Constructs a TODO item with its fields. * Constructs a TODO item with its fields.
* Params: * Params:
* fname = the file where the item is located. mandatory. * fname = the file where the _TODO comment_ is located. mandatory.
* line = the line where the item is located. mandatory. * line = the line where the _TODO comment_ is located. mandatory.
* text = the TODO text. mandatory. * text = the _TODO comment_ main text. mandatory.
* cat = the _TODO comment_ category, optional.
* ass = the _TODO comment_ assignee, optional.
* prior = the _TODO comment_ priority, as an integer litteral, optional.
* status = the _TODO comment_ status, optional.
*/ */
@safe public this(string fname, string line, string text, string cat = "", string ass = "", string prior = "", string status = "") @safe public this(string fname, string line, string text, string cat = "", string ass = "", string prior = "", string status = "")
{ {
@ -93,8 +108,8 @@ private alias TodoItems = TodoItem * [];
* Params: * Params:
* args = a list of files to analyze. * args = a list of files to analyze.
* Called each time a document is focused. Args is set using: * Called each time a document is focused. Args is set using:
* - the symbolic string <CFF> (current file is not in a project). * - the symbolic string `<CFF>` (current file is not in a project).
* - the symbolic string <CPFS> (current file is in a project). * - the symbolic string `<CPFS>` (current file is in a project).
*/ */
void main(string[] args) void main(string[] args)
{ {
@ -111,12 +126,22 @@ void main(string[] args)
auto config = LexerConfig(f, StringBehavior.source); auto config = LexerConfig(f, StringBehavior.source);
StringCache cache = StringCache(StringCache.defaultBucketCount); StringCache cache = StringCache(StringCache.defaultBucketCount);
auto lexer = DLexer(src, config, &cache); auto lexer = DLexer(src, config, &cache);
// analyze the tokens // analyze the tokens
foreach(tok; lexer) token2TodoItem(tok, f, todoItems); foreach(tok; lexer) token2TodoItem(tok, f, todoItems);
} }
// serialize the items using the pascal component streaming text format // serialize the items using the pascal component streaming text format
foreach(todoItem; todoItems) todoItem.serialize(LfmString); foreach(todoItem; todoItems) todoItem.serialize(LfmString);
// the widget has the TODOs in the output
// separates the data sent in procOutput() from those sent in procTerminated()
// TODO-cbugfix: find a way to determine if stdout has been written
if (stdout.size != 0) {
stdout.flush;
readln;
}
// the widget has the LFM script in the output, it reads in procTerminated()
if (LfmString.length) writefln("object TTodoItems\n items = <\n%s>\nend", LfmString); if (LfmString.length) writefln("object TTodoItems\n items = <\n%s>\nend", LfmString);
} }

View File

@ -6,11 +6,13 @@ interface
uses uses
Classes, SysUtils, FileUtil, TreeFilterEdit, ListFilterEdit, Forms, Controls, Classes, SysUtils, FileUtil, TreeFilterEdit, ListFilterEdit, Forms, Controls,
Graphics, Dialogs, ExtCtrls, Menus, Buttons, StdCtrls, ComCtrls, ce_widget, Graphics, Dialogs, ExtCtrls, Menus, Buttons, StdCtrls, ComCtrls, asyncprocess,
process, ce_common, ce_interfaces, ce_synmemo, ce_project, ce_symstring; ce_widget, process, ce_common, ce_interfaces, ce_synmemo, ce_project, ce_symstring;
type type
TTodoContext = (tcNone, tcProject, tcFile);
// represents a TODO item // represents a TODO item
// warning: the props names must be kept in sync with the values set in the tool. // warning: the props names must be kept in sync with the values set in the tool.
TTodoItem = class(TCollectionItem) TTodoItem = class(TCollectionItem)
@ -64,6 +66,7 @@ type
fDoc: TCESynMemo; fDoc: TCESynMemo;
fToolProcess: TCheckedAsyncProcess; fToolProcess: TCheckedAsyncProcess;
fTodos: TTodoItems; fTodos: TTodoItems;
fLogMessager: TCELogMessageSubject;
// ICEMultiDocObserver // ICEMultiDocObserver
procedure docNew(aDoc: TCESynMemo); procedure docNew(aDoc: TCESynMemo);
procedure docFocused(aDoc: TCESynMemo); procedure docFocused(aDoc: TCESynMemo);
@ -76,6 +79,7 @@ type
procedure projFocused(aProject: TCEProject); procedure projFocused(aProject: TCEProject);
procedure projCompiling(aProject: TCEProject); procedure projCompiling(aProject: TCEProject);
// TODOlist things // TODOlist things
function getContext: TTodoContext;
procedure killToolProcess; procedure killToolProcess;
procedure callToolProcess; procedure callToolProcess;
procedure procTerminated(sender: TObject); procedure procTerminated(sender: TObject);
@ -157,6 +161,7 @@ var
begin begin
inherited; inherited;
fTodos := TTodoItems.Create(self); fTodos := TTodoItems.Create(self);
fLogMessager := TCELogMessageSubject.create;
lstItems.OnDblClick := @lstItemsDoubleClick; lstItems.OnDblClick := @lstItemsDoubleClick;
btnRefresh.OnClick := @btnRefreshClick; btnRefresh.OnClick := @btnRefreshClick;
@ -177,6 +182,7 @@ end;
destructor TCETodoListWidget.destroy; destructor TCETodoListWidget.destroy;
begin begin
killToolProcess; killToolProcess;
fLogMessager.Free;
inherited; inherited;
end; end;
{$ENDREGION} {$ENDREGION}
@ -236,6 +242,19 @@ end;
{$ENDREGION} {$ENDREGION}
{$REGION Todo list things ------------------------------------------------------} {$REGION Todo list things ------------------------------------------------------}
function TCETodoListWidget.getContext: TTodoContext;
begin
result := tcNone;
//
if ((fProj = nil) and (fDoc = nil)) then exit;
if ((fProj = nil) and (fDoc <> nil)) then exit(tcFile);
if ((fProj <> nil) and (fDoc = nil)) then exit(tcProject);
//
result := tcFile;
if not FileExists(fDoc.fileName) then exit;
if fProj.isProjectSource(fDoc.fileName) then exit(tcProject);
end;
procedure TCETodoListWidget.killToolProcess; procedure TCETodoListWidget.killToolProcess;
begin begin
if fToolProcess = nil then exit; if fToolProcess = nil then exit;
@ -247,16 +266,14 @@ end;
procedure TCETodoListWidget.callToolProcess; procedure TCETodoListWidget.callToolProcess;
var var
asProject: boolean; ctxt: TTodoContext;
begin begin
clearTodoList; clearTodoList;
if not exeInSysPath(ToolExeName) then exit; if not exeInSysPath(ToolExeName) then exit;
ctxt := getContext;
if ctxt = tcNone then exit;
if (fDoc = nil) and (fProj = nil)then exit; if (fDoc = nil) and (fProj = nil)then exit;
// //
if (fProj <> nil) then if (fDoc = nil) then asProject := true;
if (fProj = nil) then if (fDoc <> nil) then asProject := false;
if (fProj <> nil) then if (fDoc <> nil) then asProject := (fProj.isProjectSource(fDoc.fileName));
//
killToolProcess; killToolProcess;
// process parameter // process parameter
fToolProcess := TCheckedAsyncProcess.Create(nil); fToolProcess := TCheckedAsyncProcess.Create(nil);
@ -266,16 +283,32 @@ begin
fToolProcess.OnTerminate := @procTerminated; fToolProcess.OnTerminate := @procTerminated;
fToolProcess.OnReadData := @procOutput; fToolProcess.OnReadData := @procOutput;
// files passed to the tool argument // files passed to the tool argument
if asProject then fToolProcess.Parameters.Add(symbolExpander.get('<CPFS>')) if ctxt = tcProject then fToolProcess.Parameters.Add(symbolExpander.get('<CPFS>'))
else fToolProcess.Parameters.AddText(symbolExpander.get('<CFF>')); else fToolProcess.Parameters.AddText(symbolExpander.get('<CFF>'));
// //
fToolProcess.Execute; fToolProcess.Execute;
end; end;
procedure TCETodoListWidget.procOutput(sender: TObject); procedure TCETodoListWidget.procOutput(sender: TObject);
var
str: TStringList;
msg: string;
ctxt: TTodoContext;
begin begin
// output during run-time subjLmFromString(fLogMessager, 'called even if nothing in output', fProj, amcProj, amkAuto);
// should not be called str := TStringList.Create;
try
processOutputToStrings(TAsyncProcess(fToolProcess), str);
ctxt := getContext;
for msg in str do case ctxt of
tcNone: subjLmFromString(fLogMessager, msg, nil, amcMisc, amkAuto);
tcFile: subjLmFromString(fLogMessager, msg, fDoc, amcEdit, amkAuto);
tcProject:subjLmFromString(fLogMessager, msg, fProj, amcProj, amkAuto);
end;
finally
str.Free;
end;
fToolProcess.Input.WriteByte($0A);
end; end;
procedure TCETodoListWidget.procTerminated(sender: TObject); procedure TCETodoListWidget.procTerminated(sender: TObject);