diff --git a/README.md b/README.md index 3abb7da6..d1264360 100644 --- a/README.md +++ b/README.md @@ -12,23 +12,24 @@ Current features - instant run (without saving, script-like). - synchronized edition in a block. - D syntax highlighter, folding, identifier markup. -- current module member list. +- member list of the current module. +- manager for the static libraries. - search and replace. +- [D Completion Daemon](https://github.com/Hackerpilot/DCD) integration for completion proposal and source code hints. +- mini file browser. -Planed in version 1 -------------------- -- project configurations templates (release, debug, etc.). -- basic auto completion (brackets, key-words, ...). -- console input handling. -- static library explorer (using JSON infos). +Missing features before the first beta +-------------------------------------- +- Options editor. (the big missing stuff) +- console input handling. (workarounds exists) +- project configurations templates (release, debug, etc.). (detail) Project information ------------------- - state: alpha 4. - license: MIT. -- programmed in Pascal with [Lazarus](http://www.lazarus.freepascal.org). -- based on *dmd* (*gdc* or *lmd* characteristics are not handled). -- no other third part dependencies (so far...but using *dscanner* and/or *dcd* is envisaged.) +- programmed in Pascal with [Lazarus](http://www.lazarus.freepascal.org) as IDE. +- based on *dmd* (*gdc* or *lmd* switches are not handled). Setup & test ------------ @@ -37,7 +38,8 @@ The complete procedure is described in the first section of the [wiki](https://g Preview ------- -Windows version: +Windows version (Windows 7, x86): ![Win screen-cap](lazproj/Gui.tease.png "Coedit GUI preview") -Linux version: + +Linux version (OpenSuse 13.1, x86_64): ![Nux screen-cap](lazproj/Gui.tease.kde.png "Coedit GUI preview") \ No newline at end of file diff --git a/lazproj/Gui.tease.kde.png b/lazproj/Gui.tease.kde.png index 5f876a29..578c2f6c 100644 Binary files a/lazproj/Gui.tease.kde.png and b/lazproj/Gui.tease.kde.png differ diff --git a/lazproj/Gui.tease.png b/lazproj/Gui.tease.png index 973549b8..4a919eed 100644 Binary files a/lazproj/Gui.tease.png and b/lazproj/Gui.tease.png differ diff --git a/lazproj/coedit.lpi b/lazproj/coedit.lpi index 97aab566..9dbad959 100644 --- a/lazproj/coedit.lpi +++ b/lazproj/coedit.lpi @@ -135,7 +135,7 @@ - + @@ -271,6 +271,11 @@ + + + + + diff --git a/lazproj/coedit.lpr b/lazproj/coedit.lpr index 04e1fe75..e6cb7d5a 100644 --- a/lazproj/coedit.lpr +++ b/lazproj/coedit.lpr @@ -7,7 +7,7 @@ uses cthreads, {$ENDIF}{$ENDIF} Interfaces, Forms, lazcontrols, runtimetypeinfocontrols, anchordockpkg, - AnchorDocking, AnchorDockStorage, AnchorDockOptionsDlg, ce_main; + AnchorDocking, AnchorDockStorage, AnchorDockOptionsDlg, ce_main, ce_dcd; {$R *.res} diff --git a/src/ce_dcd.pas b/src/ce_dcd.pas new file mode 100644 index 00000000..f68d6656 --- /dev/null +++ b/src/ce_dcd.pas @@ -0,0 +1,146 @@ +unit ce_dcd; +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, process, forms, strutils; + +(** + * Adds a folder of d sources for DCD. + *) +procedure addDcdImport(const aFilename: string); +(** + * gets a list of propositions for the identifier at aPosition in aFilename. + *) +procedure getCompletion(const aFilename: string; aPosition: Integer; const list: TStrings); +(** + * tries to get the DDoc comment for the identifier at aPosition in aFilename. + *) +procedure getHint(const aFilename: string; aPosition: Integer; const list: TStrings); + +var + DCD_server: TProcess; + DCD_client: TProcess; + lines: TStringList; + dcdOn: boolean; + +implementation + +procedure lazyServerStart; +begin + if not DCD_server.Running then + DCD_server.Execute; +end; + +procedure addDcdImport(const aFilename: string); +begin + if not dcdOn then exit; + // + if not DCD_server.Running then + DCD_server.Parameters.Add('-I'+ aFilename) + else if DCD_client <> nil then begin + DCD_client.Parameters.Clear; + DCD_client.Parameters.Add('-I'+ aFilename); + DCD_client.Execute; + end; +end; + +procedure getCompletion(const aFilename: string; aPosition: Integer; const list: TStrings); +var + i: NativeInt; + kind: Char; + item: string; +begin + if not dcdOn then exit; + lazyServerStart; + // + DCD_client.Parameters.Clear; + DCD_client.Parameters.Add('-c'); + DCD_client.Parameters.Add(intToStr(aPosition)); + DCD_client.Parameters.Add(aFilename); + DCD_client.Execute; + // + lines.LoadFromStream(DCD_client.Output); + list.Clear; + for i := 1 to lines.Count-1 do + begin + item := lines.Strings[i]; + kind := item[length(item)]; + setLength(item, length(item)-2); + case kind of + 'c': item += ' (class) '; + 'i': item += ' (interface) '; + 's': item += ' (struct) '; + 'u': item += ' (union) '; + 'v': item += ' (variable) '; + 'm': item += ' (member) '; + 'k': item += ' (reserved word) '; + 'f': item += ' (function) '; + 'g': item += ' (enum) '; + 'e': item += ' (enum member) '; + 'P': item += ' (package) '; + 'M': item += ' (module) '; + 'a': item += ' (array) '; + 'A': item += ' (associative array)'; + 'l': item += ' (alias) '; + 't': item += ' (template) '; + 'T': item += ' (mixin) '; + end; + list.Add(item); + end; +end; + +procedure getHint(const aFilename: string; aPosition: Integer; const list: TStrings); +var + i: Integer; + str: string; +begin + if not dcdOn then exit; + lazyServerStart; + // + if DCD_client.Running then exit; + + DCD_client.Parameters.Clear; + DCD_client.Parameters.Add('-c'); + DCD_client.Parameters.Add(intToStr(aPosition)); + DCD_client.Parameters.Add('-d'); + DCD_client.Parameters.Add(aFilename); + DCD_client.Execute; + // + list.LoadFromStream(DCD_client.Output); + for i := 0 to list.Count-1 do + begin + str := list.Strings[i]; + list.Strings[i] := ReplaceStr(str, '\n', ''); + end; +end; + + +initialization + DCD_server := TProcess.Create(nil); + DCD_client := TProcess.Create(nil); + DCD_server.Executable := extractFilePath(application.ExeName) + directorySeparator + + 'dcd-server'{$IFDEF WINDOWS}+ '.exe'{$ENDIF}; + DCD_client.Executable := extractFilePath(application.ExeName) + directorySeparator + + 'dcd-client'{$IFDEF WINDOWS}+ '.exe'{$ENDIF}; + DCD_client.Options := [poUsePipes{$IFDEF WINDOWS}, poNewConsole{$ENDIF}]; + DCD_server.Options := [poUsePipes{$IFDEF WINDOWS}, poNewConsole{$ENDIF}]; + DCD_client.ShowWindow := swoHIDE; + DCD_server.ShowWindow := swoHIDE; + dcdOn := fileExists(DCD_server.Executable) and fileExists(DCD_client.Executable); + lines := TStringList.Create; + {$IFDEF WINDOWS} + // phobos + runtime + {$ENDIF} + {$IFDEF POSIX} + // phobos + runtime + {$ENDIF} +finalization + DCD_server.Active := false; + DCD_client.Active := false; + DCD_server.Free; + DCD_client.Free; + lines.Free; +end. + diff --git a/src/ce_editor.lfm b/src/ce_editor.lfm index f66539e6..9a5095c4 100644 --- a/src/ce_editor.lfm +++ b/src/ce_editor.lfm @@ -95,4 +95,17 @@ inherited CEEditorWidget: TCEEditorWidget 0000000000000000000000000000 } end + object completion: TSynCompletion[4] + OnExecute = completionExecute + Position = 0 + LinesInWindow = 6 + SelectedColor = clHighlight + CaseSensitive = True + Width = 262 + ShortCut = 16416 + EndOfTokenChr = '()[].' + OnCodeCompletion = completionCodeCompletion + ExecCommandID = ecSynCompletionExecute + left = 96 + end end diff --git a/src/ce_editor.pas b/src/ce_editor.pas index a3e7e69b..1ed4d384 100644 --- a/src/ce_editor.pas +++ b/src/ce_editor.pas @@ -8,15 +8,23 @@ uses Classes, SysUtils, FileUtil, ExtendedNotebook, Forms, Controls, lcltype, Graphics, SynEditKeyCmds, ComCtrls, SynEditHighlighter, ExtCtrls, Menus, SynEditHighlighterFoldBase, SynMacroRecorder, SynPluginSyncroEdit, SynEdit, - SynHighlighterLFM, AnchorDocking, ce_widget, ce_d2syn, ce_synmemo, ce_dlang, - ce_project; + SynHighlighterLFM, SynCompletion, AnchorDocking, ce_widget, ce_d2syn, + ce_synmemo, ce_dlang, ce_project, ce_common, types, ce_dcd; type + + { TCEEditorWidget } + TCEEditorWidget = class(TCEWidget) imgList: TImageList; PageControl: TExtendedNotebook; macRecorder: TSynMacroRecorder; editorStatus: TStatusBar; + completion: TSynCompletion; + procedure completionCodeCompletion(var Value: string; SourceValue: string; + var SourceStart, SourceEnd: TPoint; KeyChar: TUTF8Char; Shift: TShiftState + ); + procedure completionExecute(Sender: TObject); procedure PageControlChange(Sender: TObject); procedure PageControlCloseTabClicked(Sender: TObject); protected @@ -39,12 +47,14 @@ type function getEditor(index: NativeInt): TCESynMemo; function getEditorCount: NativeInt; function getEditorIndex: NativeInt; + procedure getCompletionList; public constructor create(aOwner: TComponent); override; destructor destroy; override; procedure addEditor; procedure removeEditor(const aIndex: NativeInt); procedure focusedEditorChanged; + function getEditorHint: string; // procedure projCompile(const aProject: TCEProject); override; procedure projRun(const aProject: TCEProject); override; @@ -55,6 +65,7 @@ type property editorIndex: NativeInt read getEditorIndex; end; + implementation {$R *.lfm} @@ -117,6 +128,7 @@ begin curr := getCurrentEditor; macRecorder.Editor := curr; fSyncEdit.Editor := curr; + completion.Editor := curr; // if pageControl.ActivePageIndex <> -1 then CEMainForm.docFocusedNotify(Self, pageControl.ActivePageIndex); @@ -134,6 +146,19 @@ begin focusedEditorChanged; end; +procedure TCEEditorWidget.completionExecute(Sender: TObject); +begin + getCompletionList +end; + +procedure TCEEditorWidget.completionCodeCompletion(var Value: string; + SourceValue: string; var SourceStart, SourceEnd: TPoint; KeyChar: TUTF8Char; + Shift: TShiftState); +begin + // warning: '20' depends on ce_dcd, case knd of... + Value := Value[1..length(Value)-20]; +end; + procedure TCEEditorWidget.PageControlCloseTabClicked(Sender: TObject); begin // closeBtn not implemented (Win.) @@ -219,6 +244,84 @@ begin stopUpdateByDelay; end; +procedure TCEEditorWidget.getCompletionList; +var + curr: TCESynMemo; + str: TMemoryStream; + srcpos, i: NativeInt; + fname: string; +begin + if not dcdOn then exit; + // + curr := getCurrentEditor; + if curr = nil then exit; + // + str := TMemoryStream.Create; + try + completion.Position := 0; // previous index could cause an error here. + fname := GetTempDir(false) + 'temp_' + uniqueObjStr(curr) + '.d'; + curr.Lines.SaveToStream(str); + str.SaveToFile(fname); + try + srcpos := 0; + for i := 0 to curr.LogicalCaretXY.y-2 do + begin + srcPos += length(curr.Lines.Strings[i]); + if curr.LogicalCaretXY.y <> 0 then + srcPos += length(LineEnding); + end; + srcpos += curr.LogicalCaretXY.x -1; + completion.ItemList.Clear; + ce_dcd.getCompletion(fname, srcpos, completion.ItemList); + finally + DeleteFile(fname); + end; + finally + str.Free; + end; +end; + +function TCEEditorWidget.getEditorHint: string; +var + curr: TCESynMemo; + str: TMemoryStream; + lst: TStringList; + srcpos, i: NativeInt; + fname: string; +begin + result := ''; + if not dcdOn then exit; + // + curr := getCurrentEditor; + if curr = nil then exit; + // + str := TMemoryStream.Create; + lst := TStringList.Create; + try + fname := GetTempDir(false) + 'temp_' + uniqueObjStr(curr) + '.d'; + curr.Lines.SaveToStream(str); + try + str.SaveToFile(fname); + srcpos := 0; + for i := 0 to curr.LogicalCaretXY.y-2 do + begin + srcPos += length(curr.Lines.Strings[i]); + if curr.LogicalCaretXY.y <> 0 then + srcPos += length(LineEnding); + end; + srcpos += curr.LogicalCaretXY.x -1; + if curr.GetWordAtRowCol(curr.LogicalCaretXY) <> '' then + ce_dcd.getHint(fname, srcpos, lst); + result := lst.Text; + finally + DeleteFile(fname); + end; + finally + str.Free; + lst.Free; + end; +end; + procedure TCEEditorWidget.UpdateByEvent; const modstr: array[boolean] of string = ('...', 'MODIFIED'); @@ -276,4 +379,3 @@ begin end; end. - diff --git a/src/ce_libman.pas b/src/ce_libman.pas index cceae002..06aaffdf 100644 --- a/src/ce_libman.pas +++ b/src/ce_libman.pas @@ -5,7 +5,7 @@ unit ce_libman; interface uses - Classes, SysUtils, ce_common; + Classes, SysUtils, ce_common, ce_dcd; type @@ -25,7 +25,7 @@ type end; (** - * Represents all the D library present on this system. + * Represents all the D libraries present on this system. *) TLibraryManager = class(TComponent) private @@ -46,6 +46,8 @@ type // procedure loadFromFile(const aFilename: string); procedure saveToFile(const aFilename: string); + // + procedure updateDCD; end; implementation @@ -67,6 +69,20 @@ begin fCol.assign(aValue); end; +procedure TLibraryManager.updateDCD; +var + itm: TCollectionItem; + itmt: TLibraryItem; +begin + if not dcdOn then exit; + // + for itm in fCol do + begin + itmt := TLibraryItem(itm); + ce_dcd.addDcdImport(itmt.libSourcePath); + end; +end; + procedure TLibraryManager.getAdditionalSources(const someAliases, aList: TStrings); var itm: TCollectionItem; @@ -77,7 +93,8 @@ begin for itm in fCol do begin itmt := TLibraryItem(itm); - if someAliases.IndexOf(itmt.libAlias) = -1 then continue; + if someAliases <> nil then + if someAliases.IndexOf(itmt.libAlias) = -1 then continue; // srcs := TStringList.Create; try @@ -103,7 +120,8 @@ begin for itm in fCol do begin itmt := TLibraryItem(itm); - if someAliases.IndexOf(itmt.libAlias) = -1 then continue; + if someAliases <> nil then + if someAliases.IndexOf(itmt.libAlias) = -1 then continue; // if aList.IndexOf(itmt.libFile) <> -1 then continue; aList.Add('-I' + itmt.libFile); @@ -126,6 +144,7 @@ end; procedure TLibraryManager.loadFromFile(const aFilename: string); begin loadCompFromTxtFile(self, aFilename, @readerPropNoFound, @readerError); + updateDCD; end; procedure TLibraryManager.saveToFile(const aFilename: string); diff --git a/src/ce_libmaneditor.pas b/src/ce_libmaneditor.pas index c5648a2d..ba800037 100644 --- a/src/ce_libmaneditor.pas +++ b/src/ce_libmaneditor.pas @@ -163,6 +163,7 @@ begin itm.libFile := row.SubItems.Strings[0]; itm.libSourcePath := row.SubItems.Strings[1]; end; + LibraryManager.updateDCD; end; end; diff --git a/src/ce_main.lfm b/src/ce_main.lfm index 17dde080..eeb50b6b 100644 --- a/src/ce_main.lfm +++ b/src/ce_main.lfm @@ -2912,10 +2912,11 @@ object CEMainForm: TCEMainForm } end object ApplicationProperties1: TApplicationProperties - HintHidePause = 1000 - HintPause = 25 - HintShortPause = 8 + HintHidePause = 4000 + HintPause = 100 + HintShortPause = 50 OnException = ApplicationProperties1Exception + OnShowHint = ApplicationProperties1ShowHint left = 96 end object LfmSyn: TSynLFMSyn diff --git a/src/ce_main.pas b/src/ce_main.pas index aa592354..722b2a87 100644 --- a/src/ce_main.pas +++ b/src/ce_main.pas @@ -7,10 +7,10 @@ interface uses Classes, SysUtils, FileUtil, SynEditKeyCmds, SynHighlighterLFM, Forms, AnchorDocking, AnchorDockStorage, AnchorDockOptionsDlg, Controls, Graphics, - Dialogs, Menus, ActnList, ExtCtrls, process, XMLPropStorage, ComCtrls, - ce_common, ce_dmdwrap, ce_project, ce_plugin, ce_synmemo, ce_widget, ce_messages, - ce_widgettypes, ce_editor, ce_projinspect, ce_projconf, ce_staticexplorer, ce_search, - ce_miniexplorer, dynlibs, ce_libman, ce_libmaneditor; + Dialogs, Menus, ActnList, ExtCtrls, process, XMLPropStorage, ComCtrls, dynlibs, + ce_common, ce_dmdwrap, ce_project, ce_dcd, ce_plugin, ce_synmemo, ce_widget, + ce_messages, ce_widgettypes, ce_editor, ce_projinspect, ce_projconf, ce_search, + ce_staticexplorer, ce_miniexplorer, ce_libman, ce_libmaneditor; type @@ -194,6 +194,8 @@ type procedure actProjSourceExecute(Sender: TObject); procedure actEdUnIndentExecute(Sender: TObject); procedure ApplicationProperties1Exception(Sender: TObject; E: Exception); + procedure ApplicationProperties1ShowHint(var HintStr: string; + var CanShow: Boolean; var HintInfo: THintInfo); procedure FormCloseQuery(Sender: TObject; var CanClose: boolean); procedure FormDropFiles(Sender: TObject; const FileNames: array of String); private @@ -708,6 +710,15 @@ begin // srcLst.Clear; end; + +procedure TCEMainForm.ApplicationProperties1ShowHint(var HintStr: string; + var CanShow: Boolean; var HintInfo: THintInfo); +begin + CanShow := true; + if EditWidget.currentEditor <> nil then + if EditWidget.currentEditor.Focused then + HintStr := EditWidget.getEditorHint; +end; {$ENDREGION} {$REGION file ------------------------------------------------------------------} @@ -1098,6 +1109,8 @@ begin dmdproc.Parameters.Add('-w'); dmdproc.Parameters.Add('-wi'); dmdproc.Parameters.Add('-of' + fname {$IFDEF WINDOWS}+ '.exe'{$ENDIF}); + LibraryManager.getAdditionalSources(nil, dmdproc.Parameters); + LibraryManager.getAdditionalImport(nil, dmdproc.Parameters); dmdproc.Execute; repeat ProcessOutputToMsg(dmdproc, mcEditor) until not dmdproc.Running; if (dmdProc.ExitStatus = 0) then diff --git a/src/ce_staticexplorer.pas b/src/ce_staticexplorer.pas index 2d9e7420..dc26a609 100644 --- a/src/ce_staticexplorer.pas +++ b/src/ce_staticexplorer.pas @@ -395,10 +395,11 @@ begin dmdproc.Parameters.Add('-I' + fProj.getAbsoluteSourceName(i)); for nme in fProj.currentConfiguration.pathsOptions.Includes do dmdproc.Parameters.Add('-I' + nme); - with CEMainForm do begin - Librarymanager.getAdditionalSources( fProj.LibraryAliases, dmdproc.Parameters); - Librarymanager.getAdditionalImport( fProj.LibraryAliases, dmdproc.Parameters); - end; + end; + //adds all the libman entries + with CEMainForm do begin + Librarymanager.getAdditionalSources(nil, dmdproc.Parameters); + Librarymanager.getAdditionalImport(nil, dmdproc.Parameters); end; // dmdproc.Execute; diff --git a/src/ce_synmemo.pas b/src/ce_synmemo.pas index dcbf6640..81cd6bce 100644 --- a/src/ce_synmemo.pas +++ b/src/ce_synmemo.pas @@ -50,7 +50,7 @@ var implementation uses - graphics, ce_main, forms, ExtendedNotebook, comctrls; + graphics, ce_main, forms; constructor TCESynMemo.Create(aOwner: TComponent); begin @@ -74,7 +74,7 @@ begin // fFilename := ''; fModified := false; - + ShowHint := true; TextBuffer.AddNotifyHandler(senrUndoRedoAdded, @changeNotify); end;