diff --git a/cetodo/cetodo.coedit b/cetodo/cetodo.coedit index 369aa340..0e0b8358 100644 --- a/cetodo/cetodo.coedit +++ b/cetodo/cetodo.coedit @@ -18,7 +18,6 @@ object CurrentProject: TCEProject end item name = 'release' - outputOptions.inlining = True outputOptions.boundsCheck = offAlways outputOptions.optimizations = True outputOptions.release = True @@ -33,7 +32,7 @@ object CurrentProject: TCEProject Sources.Strings = ( 'cetodo.d' ) - ConfigurationIndex = 0 + ConfigurationIndex = 1 LibraryAliases.Strings = ( 'libdparse' ) diff --git a/cetodo/cetodo.d b/cetodo/cetodo.d index 56c18a8e..ac541547 100644 --- a/cetodo/cetodo.d +++ b/cetodo/cetodo.d @@ -1,32 +1,39 @@ /******************************************************************************* 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 case sensitive. - fields: an optional list of property with a format similar to the execution argument -of a program: --. +of a program: `--`. possible fields include: - - c: TODO category, e.g: -cserialization -cpersistence -cerrorhandling - - a: TODO assignee, e.g: -aMisterFreeze -aMadameMichou -aJhonSmith - - p: TODO priority, eg: -p8 -p0 - - s: TODO status, e.g -sPartiallyFixed, -sDone + - c: TODO category, e.g: _-cserialization_, _-cpersistence_, _ -cerrorhandling_. + - a: TODO assignee, e.g: _-aMisterFreeze_, _-aMadameMichou_, _-aJhonSmith_. + - p: TODO priority, eg: _-p8_, _-p0_. + - s: TODO status, e.g _-sPartiallyFixed_, _-sDone_. - 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-cfeature: save this property in the inifile. -// TODO-cannnotations-p8: annotates the member of the module as @safe and if possible nothrow. +``// TODO: set this property as const() to make it read-only.`` -- 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 -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) +``// TODO-cannnotations-p8: annotate the members of the module with @safe and if possible nothrow.`` + +## 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; @@ -38,18 +45,26 @@ import std.file, std.path, std.range; // libdparse import std.d.ast, std.d.lexer, std.d.parser; - +/// Encapsulates the fields of a _TODO comment_. 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 string[TodoField] fFields; /** * Constructs a TODO item with its fields. * Params: - * fname = the file where the item is located. mandatory. - * line = the line where the item is located. mandatory. - * text = the TODO text. mandatory. + * fname = the file where the _TODO comment_ is located. mandatory. + * line = the line where the _TODO comment_ is located. 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 = "") { @@ -93,8 +108,8 @@ private alias TodoItems = TodoItem * []; * Params: * args = a list of files to analyze. * Called each time a document is focused. Args is set using: - * - the symbolic string (current file is not in a project). - * - the symbolic string (current file is in a project). + * - the symbolic string `` (current file is not in a project). + * - the symbolic string `` (current file is in a project). */ void main(string[] args) { @@ -111,12 +126,22 @@ void main(string[] args) auto config = LexerConfig(f, StringBehavior.source); StringCache cache = StringCache(StringCache.defaultBucketCount); auto lexer = DLexer(src, config, &cache); + // analyze the tokens foreach(tok; lexer) token2TodoItem(tok, f, todoItems); } + // serialize the items using the pascal component streaming text format 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); } diff --git a/src/ce_todolist.pas b/src/ce_todolist.pas index 9fb1e2e9..994ac0b0 100644 --- a/src/ce_todolist.pas +++ b/src/ce_todolist.pas @@ -6,11 +6,13 @@ interface uses Classes, SysUtils, FileUtil, TreeFilterEdit, ListFilterEdit, Forms, Controls, - Graphics, Dialogs, ExtCtrls, Menus, Buttons, StdCtrls, ComCtrls, ce_widget, - process, ce_common, ce_interfaces, ce_synmemo, ce_project, ce_symstring; + Graphics, Dialogs, ExtCtrls, Menus, Buttons, StdCtrls, ComCtrls, asyncprocess, + ce_widget, process, ce_common, ce_interfaces, ce_synmemo, ce_project, ce_symstring; type + TTodoContext = (tcNone, tcProject, tcFile); + // represents a TODO item // warning: the props names must be kept in sync with the values set in the tool. TTodoItem = class(TCollectionItem) @@ -64,6 +66,7 @@ type fDoc: TCESynMemo; fToolProcess: TCheckedAsyncProcess; fTodos: TTodoItems; + fLogMessager: TCELogMessageSubject; // ICEMultiDocObserver procedure docNew(aDoc: TCESynMemo); procedure docFocused(aDoc: TCESynMemo); @@ -76,6 +79,7 @@ type procedure projFocused(aProject: TCEProject); procedure projCompiling(aProject: TCEProject); // TODOlist things + function getContext: TTodoContext; procedure killToolProcess; procedure callToolProcess; procedure procTerminated(sender: TObject); @@ -157,6 +161,7 @@ var begin inherited; fTodos := TTodoItems.Create(self); + fLogMessager := TCELogMessageSubject.create; lstItems.OnDblClick := @lstItemsDoubleClick; btnRefresh.OnClick := @btnRefreshClick; @@ -177,6 +182,7 @@ end; destructor TCETodoListWidget.destroy; begin killToolProcess; + fLogMessager.Free; inherited; end; {$ENDREGION} @@ -236,6 +242,19 @@ end; {$ENDREGION} {$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; begin if fToolProcess = nil then exit; @@ -247,16 +266,14 @@ end; procedure TCETodoListWidget.callToolProcess; var - asProject: boolean; + ctxt: TTodoContext; begin clearTodoList; if not exeInSysPath(ToolExeName) then exit; + ctxt := getContext; + if ctxt = tcNone 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; // process parameter fToolProcess := TCheckedAsyncProcess.Create(nil); @@ -266,16 +283,32 @@ begin fToolProcess.OnTerminate := @procTerminated; fToolProcess.OnReadData := @procOutput; // files passed to the tool argument - if asProject then fToolProcess.Parameters.Add(symbolExpander.get('')) + if ctxt = tcProject then fToolProcess.Parameters.Add(symbolExpander.get('')) else fToolProcess.Parameters.AddText(symbolExpander.get('')); // fToolProcess.Execute; end; procedure TCETodoListWidget.procOutput(sender: TObject); +var + str: TStringList; + msg: string; + ctxt: TTodoContext; begin - // output during run-time - // should not be called + subjLmFromString(fLogMessager, 'called even if nothing in output', fProj, amcProj, amkAuto); + 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; procedure TCETodoListWidget.procTerminated(sender: TObject);