Quartex pascal, foundation stone laid
Managed to get a couple of hours work done on QPAS (quartex pascal) today, and finally I have reached the part where all the different sub-systems come together. Writing an IDE is not the same as writing a notepad clone or simple text editor. The concept of project item types (for instance, how do you define a unit versus a textfile? Should a file ending with .res open up in a text-editor or a hex-dump?), storage mechanisms, source templates, search paths – the list goes on. Point is, it has to be abstract enough to be flexible, but rigid enough to take a punch and provide logical structure.
So in short: Yes, the IDE can be adapted for other languages (finally we can put those synEdit syntax highlighters to good use)!
But those are just the default “low level IDE stuff”, I will also need to add support for pre-defined projects. You know when you create a new delphi project, it basically just copies an already existing project from disk? This will be the next part.
In order to make this scriptable I had to add some overhead. First, everything is neatly isolated in a core class which provides access to all the sub-systems. It was very tempting to just litter the code with functions (like “getTemplateList”) rather than coding full manager classes, but I’m in no hurry – and I have a full daytime job besides. So I decided to take my time.
Here is an example from the IDE:
function TfrmMainForm.getBufferForUnit(aIdentifier:String; var aData:String):Boolean; var mOpen:TObjectList; x: Integer; mExt: String; mName: String; mFound: TQTFileSourceRefList; mText: TStringlist; mData: TStream; mRef: String; mAccess: TIDEAccessManager; begin (* Initialize to negative *) result:=False; mOpen:=NIL; setLength(aData,0); (* Make sure we can do this *) if not (csDestroying in ComponentState) and not (csLoading in ComponentState) and not application.Terminated then begin (* Request access to IDE API *) if getIDEAccess(mAccess) then begin mExt:=ExtractFileExt(aIdentifier); (* Get buffer from open tab *) if getOpenTabsForProject(mAccess.ProjectManager.ActiveProject,mOpen) then begin try for x:=0 to mOpen.Count-1 do begin (* NOTE: replace with byterage->compare later! *) if SameText(mopen[x].Item.Identifier,aIdentifier) then begin if (mOpen[x].Frame is TframePascal) then begin aData:=TframePascal(mOpen[x].Frame).editor.Text; break; end; end; end; result:=length(aData)>0; finally mOpen.Free; end; end; if not result then begin if length(mExt)=0 then mName:=aIdentifier + '.pas' else mName:=aIdentifier; if mAccess.FileSourceManager.LocateFileByName(mName,mFound) then begin try if mFound.Count>0 then begin mtext:=TStringlist.Create; try (* Get unit candidates, sort by priority to the project *) mRef:=mAccess.FileSourceManager.SortByPriority(mFound,mAccess.ProjectManager.ActiveProject).Items; WriteMessageF('using package unit [%s]',[mRef]); try if mAccess.FileSourceManager.ObtainStreamFromRef(mRef,mData) then begin try (* Note: Replace with streamToStr before alpha *) mtext.LoadFromStream(mData); aData:=mtext.Text; result:=true; finally mData.Free; end; end; except on e: exception do TIDEExceptionHandler.HandleIOException(e,ehLogMessage); end; finally mtext.Free; end; end; finally mFound.Free; end; end; end; end; end; end;
Now to get rid of the “.pas” harcoded string (query the project.template subsystem) and rename the editor frame to something more useful. Or just go bananaz and compile with packages and isolate different editors in DLL plugins (ala eclipse).