View Issue Details

IDProjectCategoryView StatusLast Update
0022235LazarusIDEpublic2016-06-27 10:43
ReporterLudo Brands Assigned ToOndrej Pokorny  
PrioritynormalSeverityminorReproducibilityalways
Status resolvedResolutionfixed 
Product Version1.1 (SVN) 
Target Version1.6 
Summary0022235: Codetools support for unit names with dots missing.
DescriptionSeveral codetools functions are not working with unit names containing dots (Ex unit3.test.pas).
- Ctrl Space in the uses clause proposes the unit truncated at the space (unit3)
- Ctrl Enter on the complete unit name does not open the unit.
- Code completion proposes identifiers with truncated unit names. Ctrl space after unit3. will propose the identifiers or types in unit3.test. Completing manually the unit name will thereafter result in "Error: identifier not found: test"
- Find declaration does not work, same "Error: identifier not found: test"

Not sure if this is codetools related:
-units with dots are not listed in the Source/Add unit to Uses Section ...

If needed I'll make a different issue for this.
Additional Informationsvn 37593.
TagsNo tags attached.
Fixed in Revision50266, 50268
LazTarget-
Widgetset
Attached Files

Relationships

related to 0029486 closedOndrej Pokorny r50266 broke the "Unused units..." feature 
related to 0029046 resolvedOndrej Pokorny ctrl-enter (open unit) doesn't respect dotted unit names 

Activities

Ondrej Pokorny

2015-10-16 15:44

developer  

ct-namespaces-1.patch (40,637 bytes)   
Index: components/codetools/basiccodetools.pas
===================================================================
--- components/codetools/basiccodetools.pas	(revision 50072)
+++ components/codetools/basiccodetools.pas	(working copy)
@@ -41,7 +41,7 @@
 
 uses
   Classes, SysUtils, AVL_Tree, SourceLog, KeywordFuncLists, FileProcs,
-  LazFileUtils, LazUTF8;
+  LazFileUtils, LazUTF8, strutils;
 
 //----------------------------------------------------------------------------
 { These functions are used by the codetools }
@@ -225,22 +225,39 @@
   private
     FFilename: string;
     FUnitName: string;
+    function GetFileUnitNameWithoutNamespace: string;
   public
     constructor Create(const TheUnitName, TheFilename: string);
     property FileUnitName: string read FUnitName;
+    property FileUnitNameWithoutNamespace: string read GetFileUnitNameWithoutNamespace;
     property Filename: string read FFilename;
   end;
 
+  TNameSpaceInfo = class
+  public
+    NameSpace: string;
+  end;
+
+procedure AddToTreeOfUnitFilesOrNamespaces(
+  var TreeOfUnitFiles, TreeOfNameSpaces: TAVLTree;
+  const NameSpacePath, Filename: string;
+  CaseInsensitive, KeepDoubles: boolean);
 function GatherUnitFiles(const BaseDir, SearchPath,
-    Extensions: string; KeepDoubles, CaseInsensitive: boolean;
-    var TreeOfUnitFiles: TAVLTree): boolean;
+    Extensions, NameSpacePath: string; KeepDoubles, CaseInsensitive: boolean;
+    var TreeOfUnitFiles, TreeOfNamespaces: TAVLTree): boolean;
 procedure FreeTreeOfUnitFiles(TreeOfUnitFiles: TAVLTree);
 procedure AddToTreeOfUnitFiles(var TreeOfUnitFiles: TAVLTree;
   const Filename: string;
   KeepDoubles: boolean);
+procedure AddToTreeOfNamespaces(var TreeOfNameSpaces: TAVLTree;
+  const UnitName, ParentNameSpacePath: string;
+  KeepDoubles: boolean);
 function CompareUnitFileInfos(Data1, Data2: Pointer): integer;
+function CompareNameSpaceInfos(Data1, Data2: Pointer): integer;
 function CompareUnitNameAndUnitFileInfo(UnitnamePAnsiString,
                                         UnitFileInfo: Pointer): integer;
+function CompareNameSpaceAndNameSpaceInfo(NamespacePAnsiString,
+                                        NamespaceInfo: Pointer): integer;
 
 //-----------------------------------------------------------------------------
 // functions / procedures
@@ -5636,9 +5653,53 @@
   System.Move(p^,Result[1],l);
 end;
 
-function GatherUnitFiles(const BaseDir, SearchPath,
-  Extensions: string; KeepDoubles, CaseInsensitive: boolean;
-  var TreeOfUnitFiles: TAVLTree): boolean;
+procedure AddToTreeOfUnitFilesOrNamespaces(var TreeOfUnitFiles,
+  TreeOfNameSpaces: TAVLTree; const NameSpacePath, Filename: string;
+  CaseInsensitive, KeepDoubles: boolean);
+
+  procedure FileAndNameSpaceFits(const UnitName: string; out FileNameFits, NameSpaceFits: Boolean);
+  var
+    CompareCaseInsensitive: Boolean;
+  begin
+    FileNameFits := False;
+    NameSpaceFits := False;
+    if NameSpacePath = '' then begin
+      //we search for files without namespace path
+      FileNameFits := pos('.', UnitName) = 0;
+      NameSpaceFits := not FileNameFits;
+      Exit;
+    end;
+    if Length(UnitName) < Length(NameSpacePath) then Exit;
+
+    CompareCaseInsensitive:=CaseInsensitive;
+    {$IFDEF Windows}
+    CompareCaseInsensitive:=true;
+    {$ENDIF}
+
+    if CompareText(PChar(UnitName), Length(NameSpacePath), PChar(NameSpacePath), Length(NameSpacePath), not CompareCaseInsensitive) = 0 then
+    begin
+      FileNameFits := PosEx('.', UnitName, Length(NameSpacePath)+1) = 0;
+      NameSpaceFits := not FileNameFits;
+    end;
+  end;
+
+var
+  FileNameFits, NameSpaceFits: Boolean;
+  UnitName: string;
+begin
+  UnitName := ExtractFileNameOnly(Filename);
+  FileAndNameSpaceFits(UnitName, FileNameFits, NameSpaceFits);
+  if FileNameFits then
+    AddToTreeOfUnitFiles(TreeOfUnitFiles,FileName,
+                       KeepDoubles);
+  if NameSpaceFits then
+    AddToTreeOfNamespaces(TreeOfNamespaces,UnitName,NameSpacePath,
+                          KeepDoubles)
+end;
+
+function GatherUnitFiles(const BaseDir, SearchPath, Extensions,
+  NameSpacePath: string; KeepDoubles, CaseInsensitive: boolean;
+  var TreeOfUnitFiles, TreeOfNamespaces: TAVLTree): boolean;
 // BaseDir: base directory, used when SearchPath is relative
 // SearchPath: semicolon separated list of directories
 // Extensions: semicolon separated list of extensions (e.g. 'pas;.pp;ppu')
@@ -5645,6 +5706,7 @@
 // KeepDoubles: false to return only the first match of each unit
 // CaseInsensitive: true to ignore case on comparing extensions
 // TreeOfUnitFiles: tree of TUnitFileInfo
+// TreeOfNamespaces: tree of TNameSpaceInfo
 var
   SearchedDirectories: TAVLTree; // tree of AnsiString
 
@@ -5759,9 +5821,10 @@
         if (FileInfo.Name='.') or (FileInfo.Name='..') or (FileInfo.Name='')
         then
           continue;
-        if ExtensionFits(FileInfo.Name) then begin
-          AddToTreeOfUnitFiles(TreeOfUnitFiles,ADirectory+FileInfo.Name,
-                               KeepDoubles);
+        if ExtensionFits(FileInfo.Name) then
+        begin
+          AddToTreeOfUnitFilesOrNamespaces(TreeOfUnitFiles, TreeOfNamespaces,
+            NameSpacePath, ADirectory+FileInfo.Name, CaseInsensitive, KeepDoubles);
         end;
       until FindNextUTF8(FileInfo)<>0;
     end;
@@ -5832,6 +5895,35 @@
   TreeOfUnitFiles.Add(NewItem);
 end;
 
+procedure AddToTreeOfNamespaces(var TreeOfNameSpaces: TAVLTree; const UnitName,
+  ParentNameSpacePath: string; KeepDoubles: boolean);
+var
+  AnNameSpace: String;
+  NewItem: TNameSpaceInfo;
+  PointPos: Integer;
+begin
+  PointPos := PosEx('.', UnitName, Length(ParentNameSpacePath)+1);
+  if PointPos = 0 then Exit;
+  AnNameSpace:=Copy(UnitName, Length(ParentNameSpacePath)+1, PointPos - Length(ParentNameSpacePath) - 1);
+  if AnNameSpace = '' then Exit;
+  if (not KeepDoubles) then begin
+    if (TreeOfNameSpaces<>nil)
+    and (TreeOfNameSpaces.FindKey(Pointer(AnNameSpace),
+                                 @CompareNameSpaceAndNameSpaceInfo)<>nil)
+    then begin
+      // a namespace with the same name was already found and doubles are not
+      // wanted
+      exit;
+    end;
+  end;
+  // add
+  if TreeOfNameSpaces=nil then
+    TreeOfNameSpaces:=TAVLTree.Create(@CompareNameSpaceInfos);
+  NewItem:=TNameSpaceInfo.Create;
+  NewItem.NameSpace := AnNameSpace;
+  TreeOfNameSpaces.Add(NewItem);
+end;
+
 function CompareUnitFileInfos(Data1, Data2: Pointer): integer;
 begin
   Result:=CompareIdentifiers(PChar(TUnitFileInfo(Data1).FileUnitName),
@@ -5838,13 +5930,28 @@
                              PChar(TUnitFileInfo(Data2).FileUnitName));
 end;
 
+function CompareNameSpaceInfos(Data1, Data2: Pointer): integer;
+begin
+  Result:=CompareIdentifiers(PChar(TNameSpaceInfo(Data1).NameSpace),
+                             PChar(TNameSpaceInfo(Data2).NameSpace));
+end;
+
 function CompareUnitNameAndUnitFileInfo(UnitnamePAnsiString,
   UnitFileInfo: Pointer): integer;
 begin
-  Result:=CompareIdentifiers(PChar(UnitnamePAnsiString),
+  //do not use CompareIdentifiers - they compare only to the first "."
+  Result:=CompareText(PChar(UnitnamePAnsiString),
                              PChar(TUnitFileInfo(UnitFileInfo).FileUnitName));
 end;
 
+function CompareNameSpaceAndNameSpaceInfo(NamespacePAnsiString,
+  NamespaceInfo: Pointer): integer;
+begin
+  //do not use CompareIdentifiers - they compare only to the first "."
+  Result:=CompareText(PChar(NamespacePAnsiString),
+                             PChar(TNameSpaceInfo(NamespaceInfo).NameSpace));
+end;
+
 function CountNeededLineEndsToAddForward(const Src: string;
   StartPos, MinLineEnds: integer): integer;
 var c:char;
@@ -5981,6 +6088,16 @@
   FFilename:=TheFilename;
 end;
 
+function TUnitFileInfo.GetFileUnitNameWithoutNamespace: string;
+var
+  LastPoint: Integer;
+begin
+  LastPoint := LastDelimiter('.', FUnitName);
+  if LastPoint > 0 then
+    Result := Copy(FUnitName, LastPoint+1, High(Integer))
+  else
+    Result := FUnitName;
+end;
 
 //=============================================================================
 
Index: components/codetools/codetoolmanager.pas
===================================================================
--- components/codetools/codetoolmanager.pas	(revision 50072)
+++ components/codetools/codetoolmanager.pas	(working copy)
@@ -472,7 +472,8 @@
 
     // gather identifiers (i.e. all visible)
     function GatherUnitNames(Code: TCodeBuffer): Boolean;
-    function GatherIdentifiers(Code: TCodeBuffer; X,Y: integer): boolean;
+    function GatherIdentifiers(Code: TCodeBuffer; X,Y: integer;
+      var IdentifierPath: string): boolean;
     function GetIdentifierAt(Code: TCodeBuffer; X,Y: integer;
           out Identifier: string): boolean;
     function IdentItemCheckHasChilds(IdentItem: TIdentifierListItem): boolean;
@@ -687,7 +688,8 @@
     function FindUnitCaseInsensitive(Code: TCodeBuffer;
                               var AnUnitName, AnUnitInFilename: string): string;
     function FindUnitSource(Code: TCodeBuffer;
-                       const AnUnitName, AnUnitInFilename: string): TCodeBuffer;
+                       const AnUnitName, AnUnitInFilename: string;
+                       AnUnitNameCanBeNamespace: Boolean = False): TCodeBuffer;
     function CreateUsesGraph: TUsesGraph;
     function FindUnusedUnits(Code: TCodeBuffer; Units: TStrings): boolean;
 
@@ -2310,7 +2312,8 @@
   end;
 end;
 
-function TCodeToolManager.GatherIdentifiers(Code: TCodeBuffer; X, Y: integer
+function TCodeToolManager.GatherIdentifiers(Code: TCodeBuffer; X, Y: integer;
+  var IdentifierPath: string
   ): boolean;
 var
   CursorPos: TCodeXYPosition;
@@ -2330,7 +2333,7 @@
   try
     FIdentifierListUpdating:=true;
     try
-      Result:=FCurCodeTool.GatherIdentifiers(CursorPos,IdentifierList);
+      Result:=FCurCodeTool.GatherIdentifiers(CursorPos,IdentifierList,IdentifierPath);
     finally
       FIdentifierListUpdating:=false;
     end;
@@ -5033,7 +5036,7 @@
 end;
 
 function TCodeToolManager.FindUnitSource(Code: TCodeBuffer; const AnUnitName,
-  AnUnitInFilename: string): TCodeBuffer;
+  AnUnitInFilename: string; AnUnitNameCanBeNamespace: Boolean): TCodeBuffer;
 begin
   Result:=nil;
   {$IFDEF CTDEBUG}
@@ -5041,7 +5044,7 @@
   {$ENDIF}
   if not InitCurCodeTool(Code) then exit;
   try
-    Result:=FCurCodeTool.FindUnitSource(AnUnitName,AnUnitInFilename,false);
+    Result:=FCurCodeTool.FindUnitSource(AnUnitName,AnUnitInFilename,false,0,AnUnitNameCanBeNamespace);
   except
     on e: Exception do HandleException(e);
   end;
Index: components/codetools/codetree.pas
===================================================================
--- components/codetools/codetree.pas	(revision 50072)
+++ components/codetools/codetree.pas	(working copy)
@@ -80,8 +80,10 @@
   ctnVarDefinition      = 21;
   ctnConstDefinition    = 22;
   ctnGlobalProperty     = 23;
-  ctnUseUnit            = 24; // StartPos=unitname, EndPos=unitname+inFilename
+  ctnUseUnit            = 24; // StartPos=unit, EndPos=unitname+inFilename, children ctnUseUnitNamespace, ctnUseUnitClearName
   ctnVarArgs            = 25;
+  ctnUseUnitNamespace   = 26; // <namespace>.clearname.pas
+  ctnUseUnitClearName   = 27; // namespace.<clearname>.pas
 
   ctnClass              = 30;
   ctnClassInterface     = 31;
@@ -194,7 +196,7 @@
                          ctnInitialization,ctnFinalization];
   AllFindContextDescs = AllIdentifierDefinitions + AllCodeSections + AllClasses +
      [ctnProcedure];
-  AllPointContexts = AllClasses+AllSourceTypes+[ctnEnumerationType,ctnInterface,ctnImplementation,ctnTypeType];
+  AllPointContexts = AllClasses+AllSourceTypes+[ctnEnumerationType,ctnInterface,ctnImplementation,ctnTypeType,ctnUseUnitNamespace,ctnUseUnitClearName];
 
 
   // CodeTreeNodeSubDescriptors
@@ -397,6 +399,8 @@
   ctnPackage: Result:='Package';
   ctnLibrary: Result:='Library';
   ctnUnit: Result:='Unit';
+  ctnUseUnitNamespace: Result:='Namespace';
+  ctnUseUnitClearName: Result:='Unit name without namespace';
   ctnInterface: Result:='Interface Section';
   ctnImplementation: Result:='Implementation';
   ctnInitialization: Result:='Initialization';
Index: components/codetools/directorycacher.pas
===================================================================
--- components/codetools/directorycacher.pas	(revision 50072)
+++ components/codetools/directorycacher.pas	(working copy)
@@ -182,11 +182,14 @@
     function FileAge(const ShortFilename: string): TCTFileAgeTime;
     function FileAttr(const ShortFilename: string): TCTDirectoryListingAttr;
     function FileSize(const ShortFilename: string): TCTDirectoryListingSize;
-    function FindUnitSource(const AUnitName: string; AnyCase: boolean): string;
+    function FindUnitSource(const AUnitName: string; AnyCase: boolean;
+                            AFindFirstStartingWithUnitName: Boolean = False): string;
     function FindUnitSourceInCleanSearchPath(const AUnitName,
-                                  SearchPath: string; AnyCase: boolean): string;
+                                  SearchPath: string; AnyCase: boolean;
+                                  AFindFirstStartingWithUnitName: Boolean = False): string;
     function FindUnitSourceInCompletePath(var AUnitName, InFilename: string;
-               AnyCase: boolean; FPCSrcSearchRequiresPPU: boolean = false): string;
+               AnyCase: boolean; FPCSrcSearchRequiresPPU: boolean = false;
+               AFindFirstStartingWithUnitName: Boolean = false): string;
     function FindCompiledUnitInCompletePath(const AnUnitname: string;
                                             AnyCase: boolean): string;
     procedure IterateFPCUnitsInSet(const Iterate: TCTOnIterateFile);
@@ -252,7 +255,8 @@
     function FindDiskFilename(const Filename: string;
                               {%H-}SearchCaseInsensitive: boolean = false): string;
     function FindUnitInDirectory(const Directory, AUnitName: string;
-                                 AnyCase: boolean = false): string;
+                                 AnyCase: boolean = false;
+                                 AFindFirstStartingWithUnitName: Boolean = false): string;
     function FindVirtualFile(const Filename: string): string;
     function FindVirtualUnit(const AUnitName: string): string;
     function FindUnitSourceInCompletePath(const Directory: string;
@@ -1006,7 +1010,7 @@
 end;
 
 function TCTDirectoryCache.FindUnitSource(const AUnitName: string;
-  AnyCase: boolean): string;
+  AnyCase: boolean; AFindFirstStartingWithUnitName: Boolean): string;
 {$IFDEF DebugDirCacheFindUnitSource}
 const
   DebugUnitName = 'IDEDialogs';
@@ -1067,7 +1071,10 @@
         break;
 
       // check if the filename fits
-      ExtStartPos:=CurFilename+length(AUnitname);
+      if AFindFirstStartingWithUnitName then
+        ExtStartPos:=CurFilename+LastDelimiter('.', CurFilename)-1
+      else
+        ExtStartPos:=CurFilename+length(AUnitname);
       {$IFDEF DebugDirCacheFindUnitSource}
       if (CompareText(AUnitName,DebugUnitName)=0) and (System.Pos(DebugDirPart,directory)>0) then
         DebugLn('TCTDirectoryCache.FindUnitSource NEXT "',CurFilename,'" ExtStart=',dbgstr(ExtStartPos^));
@@ -1109,7 +1116,8 @@
 end;
 
 function TCTDirectoryCache.FindUnitSourceInCleanSearchPath(const AUnitName,
-  SearchPath: string; AnyCase: boolean): string;
+  SearchPath: string; AnyCase: boolean; AFindFirstStartingWithUnitName: Boolean
+  ): string;
 var
   p, StartPos, l: integer;
   CurPath: string;
@@ -1132,7 +1140,7 @@
       //DebugLn('TCTDirectoryCache.FindUnitSourceInCleanSearchPath CurPath="',CurPath,'"');
       if IsAbsolute then begin
         CurPath:=AppendPathDelim(CurPath);
-        Result:=Pool.FindUnitInDirectory(CurPath,AUnitName,AnyCase);
+        Result:=Pool.FindUnitInDirectory(CurPath,AUnitName,AnyCase,AFindFirstStartingWithUnitName);
       end else if (CurPath='.') and (Directory='') then
         Result:=Pool.FindVirtualUnit(AUnitname)
       else
@@ -1145,8 +1153,8 @@
 end;
 
 function TCTDirectoryCache.FindUnitSourceInCompletePath(var AUnitName,
-  InFilename: string; AnyCase: boolean; FPCSrcSearchRequiresPPU: boolean
-  ): string;
+  InFilename: string; AnyCase: boolean; FPCSrcSearchRequiresPPU: boolean;
+  AFindFirstStartingWithUnitName: Boolean): string;
 
   function FindInFilenameLowUp(aFilename: string): string;
   begin
@@ -1238,7 +1246,7 @@
       UnitSrc:=ctdusUnitCaseInsensitive
     else
       UnitSrc:=ctdusUnitNormal;
-    if GetUnitSourceCacheValue(UnitSrc,AUnitName,Result) then begin
+    if not AFindFirstStartingWithUnitName and GetUnitSourceCacheValue(UnitSrc,AUnitName,Result) then begin
       // found in cache
       if Result<>'' then begin
         // unit found
@@ -1255,7 +1263,7 @@
       if Result='' then begin
         // search in search path
         SrcPath:=Strings[ctdcsCompleteSrcPath];
-        Result:=FindUnitSourceInCleanSearchPath(AUnitName,SrcPath,AnyCase);
+        Result:=FindUnitSourceInCleanSearchPath(AUnitName,SrcPath,AnyCase,AFindFirstStartingWithUnitName);
       end;
       if Result='' then begin
         // search in unit set
@@ -1270,7 +1278,8 @@
         {$ENDIF}
       end;
 
-      AddToCache(UnitSrc,AUnitName,Result);
+      if not AFindFirstStartingWithUnitName then
+        AddToCache(UnitSrc,AUnitName,Result);
     end;
     if Result<>'' then begin
       // improve unit name
@@ -1667,12 +1676,13 @@
 end;
 
 function TCTDirectoryCachePool.FindUnitInDirectory(const Directory,
-  AUnitName: string; AnyCase: boolean): string;
+  AUnitName: string; AnyCase: boolean; AFindFirstStartingWithUnitName: Boolean
+  ): string;
 var
   Cache: TCTDirectoryCache;
 begin
   Cache:=GetCache(Directory,true,false);
-  Result:=Cache.FindUnitSource(AUnitName,AnyCase);
+  Result:=Cache.FindUnitSource(AUnitName,AnyCase,AFindFirstStartingWithUnitName);
   if Result='' then exit;
   Result:=Cache.Directory+Result;
 end;
Index: components/codetools/finddeclarationtool.pas
===================================================================
--- components/codetools/finddeclarationtool.pas	(revision 50072)
+++ components/codetools/finddeclarationtool.pas	(working copy)
@@ -856,7 +856,8 @@
       ExceptionOnNotFound: boolean): TFindDeclarationTool;
     function FindUnitSource(const AnUnitName,
       AnUnitInFilename: string; ExceptionOnNotFound: boolean;
-      ErrorPos: integer = 0): TCodeBuffer;
+      ErrorPos: integer = 0;
+      AnUnitNameCanBeNamespace: Boolean = False): TCodeBuffer;
     function FindUnitCaseInsensitive(var AnUnitName,
                                      AnUnitInFilename: string): string;
     procedure GatherUnitAndSrcPath(var UnitPath, CompleteSrcPath: string);
@@ -1904,7 +1905,16 @@
     {$IFDEF CTDEBUG}
     DebugLn('TFindDeclarationTool.FindDeclaration D CursorNode=',NodeDescriptionAsString(CursorNode.Desc),' HasChildren=',dbgs(CursorNode.FirstChild<>nil));
     {$ENDIF}
-    if (CursorNode.Desc in [ctnUsesSection,ctnUseUnit]) then begin
+    if (CursorNode.Desc = ctnUseUnitNamespace) then begin
+      NewExprType.Desc:=xtContext;
+      NewExprType.Context.Node:=CursorNode;
+      NewExprType.Context.Tool:=Self;
+      CleanPosToCaret(CursorNode.StartPos, NewPos);
+      NewTopLine := NewPos.Y;
+      Result := True;
+      Exit;
+    end else
+    if (CursorNode.Desc in [ctnUsesSection,ctnUseUnitClearName]) then begin
       // in uses section
       //DebugLn(['TFindDeclarationTool.FindDeclaration IsUsesSection']);
       Result:=FindDeclarationInUsesSection(CursorNode,CleanCursorPos,
@@ -2464,7 +2474,10 @@
     ReadNextAtom;
     if not UpAtomIs('USES') then
       RaiseUsesExpected;
-  end;
+  end else
+  if (UsesNode.Desc = ctnUseUnitClearName) then
+    MoveCursorToNodeStart(UsesNode.Parent);
+
   repeat
     ReadNextAtom;  // read name
     if CurPos.StartPos>CleanPos then break;
@@ -2551,8 +2564,8 @@
 end;
 
 function TFindDeclarationTool.FindUnitSource(const AnUnitName,
-  AnUnitInFilename: string; ExceptionOnNotFound: boolean; ErrorPos: integer
-  ): TCodeBuffer;
+  AnUnitInFilename: string; ExceptionOnNotFound: boolean; ErrorPos: integer;
+  AnUnitNameCanBeNamespace: Boolean): TCodeBuffer;
 var
   CompiledFilename: string;
   AFilename: String;
@@ -2575,8 +2588,9 @@
   NewUnitName:=AnUnitName;
   NewInFilename:=AnUnitInFilename;
 
-  AFilename:=DirectoryCache.FindUnitSourceInCompletePath(
-                                               NewUnitName,NewInFilename,false);
+  AFilename:=
+    DirectoryCache.FindUnitSourceInCompletePath(
+      NewUnitName,NewInFilename,false,false,AnUnitNameCanBeNamespace);
   Result:=TCodeBuffer(Scanner.OnLoadSource(Self,AFilename,true));
 
   if (Result=nil) and Assigned(OnFindUsedUnit) then begin
@@ -2974,11 +2988,20 @@
         end;
       end;
 
-    ctnUseUnit:
+    ctnUseUnitNamespace:
       begin
+        // hint for unit namespace in "uses" section
+        Result += 'namespace ';
+        MoveCursorToNodeStart(Node);
+        ReadNextAtom;
+        Result := Result + GetAtom;
+      end;
+
+    ctnUseUnitClearName:
+      begin
         // hint for unit in "uses" section
         Result += 'unit ';
-        MoveCursorToNodeStart(Node);
+        MoveCursorToNodeStart(Node.Parent);
         Result := Result + ReadIdentifierWithDots;
       end
 
@@ -3686,6 +3709,55 @@
     //debugln(['SearchInHelpers END']);
   end;
 
+  function SearchInNamespaces(UsesNode, SourceNamespaceNode: TCodeTreeNode): Boolean;
+  var
+    UnitNode, ThisNamespaceNode, TargetNamespaceNode: TCodeTreeNode;
+    Match: Boolean;
+  begin
+    Result := False;
+    if UsesNode=nil then Exit;
+
+    UnitNode := UsesNode.LastChild;
+    while UnitNode<>nil do
+    begin
+      ThisNamespaceNode := SourceNamespaceNode.Parent.FirstChild;
+      TargetNamespaceNode := UnitNode.FirstChild;
+      Match := False;
+      while (ThisNamespaceNode<>nil) and (TargetNamespaceNode<>nil) do
+      begin
+        if CompareIdentifiers(
+          @Src[ThisNamespaceNode.StartPos],
+          @Src[TargetNamespaceNode.StartPos]) <> 0
+        then Break;
+
+        if (ThisNamespaceNode=SourceNamespaceNode) then
+        begin
+          Match := True;
+          Break;
+        end;
+
+        ThisNamespaceNode := ThisNamespaceNode.NextBrother;
+        TargetNamespaceNode := TargetNamespaceNode.NextBrother;
+      end;
+      if Match then
+      begin
+        //namespace paths match
+        if (TargetNamespaceNode.NextBrother<>nil)
+           and (
+             (Params.Identifier=nil) or
+              CompareSrcIdentifiers(TargetNamespaceNode.NextBrother.StartPos,Params.Identifier))
+        then begin
+          Params.SetResult(Self,TargetNamespaceNode.NextBrother);
+          Result:=CheckResult(true,true);
+          if not (fdfCollect in Flags) then
+            exit;
+        end;
+      end;
+
+      UnitNode := UnitNode.PriorBrother;
+    end;
+  end;
+
   function SearchNextNode: boolean;
   const
     AbortNoCacheResult = false;
@@ -3924,6 +3996,14 @@
     exit;
   end;
 
+  if (ContextNode.Desc=ctnUseUnitNamespace) then
+  begin
+    //search in namespaces
+    if SearchInNamespaces(FindMainUsesNode, Params.ContextNode) then exit;
+    if SearchInNamespaces(FindImplementationUsesNode, Params.ContextNode) then exit;
+    Exit;
+  end;
+
   // find class helper functions
   SearchInHelpersInTheEnd := False;
   if (fdfSearchInHelpers in Flags)
@@ -5834,6 +5914,8 @@
   ListOfPCodeXYPosition:=nil;
   BuildTreeAndGetCleanPos(CursorPos,CleanPos);
   Node:=FindDeepestNodeAtPos(CleanPos,true);
+  if Node.Desc in [ctnUseUnitNamespace,ctnUseUnitClearName] then
+    Node:=Node.Parent;
   if Node.Desc<>ctnUseUnit then
     RaiseException('This function needs the cursor at a unit in a uses clause');
   // cursor is on an used unit -> try to locate it
@@ -7046,19 +7128,20 @@
     Node:=UsesNode.LastChild;
     while Node<>nil do begin
       if (fdfCollect in Params.Flags) then begin
-        CollectResult:=DoOnIdentifierFound(Params,Node);
+        CollectResult:=DoOnIdentifierFound(Params,Node.FirstChild);
         if CollectResult=ifrAbortSearch then begin
           Result:=false;
           exit;
         end else if CollectResult=ifrSuccess then begin
           Result:=true;
-          Params.SetResult(Self,Node);
+          Params.SetResult(Self,Node.FirstChild);
           exit;
         end;
       end else if CompareSrcIdentifiers(Node.StartPos,Params.Identifier) then begin
         // the searched identifier was a uses AUnitName, point to the identifier in
         // the uses section
-        Params.SetResult(Self,Node,Node.StartPos);
+        // if the unit name has a namespace defined point to the namespace
+        Params.SetResult(Self,Node.FirstChild);
         Result:=true;
         exit;
       end;
@@ -8181,7 +8264,7 @@
     {$IFDEF ShowExprEval}
     debugln(['  FindExpressionTypeOfTerm ResolveChildren used unit -> interface node ',dbgstr(ExprType.Context.Tool.ExtractNode(ExprType.Context.Node,[]))]);
     {$ENDIF}
-    AnUnitName:=aTool.ExtractUsedUnitName(ExprType.Context.Node,@InFilename);
+    AnUnitName:=aTool.ExtractUsedUnitName(ExprType.Context.Node.Parent,@InFilename);
     NewCodeTool:=aTool.FindCodeToolForUsedUnit(AnUnitName,InFilename,true);
     NewCodeTool.BuildInterfaceIdentifierCache(true);
     NewNode:=NewCodeTool.FindInterfaceNode;
@@ -8221,7 +8304,7 @@
         ExprType.Context.Node:=ExprType.Context.Tool.GetInterfaceNode;
       end;
     end
-    else if (ExprType.Context.Node.Desc=ctnUseUnit) then begin
+    else if (ExprType.Context.Node.Desc=ctnUseUnitClearName) then begin
       // uses unit name => interface of used unit
       ResolveUseUnit;
     end
Index: components/codetools/identcompletiontool.pas
===================================================================
--- components/codetools/identcompletiontool.pas	(revision 50072)
+++ components/codetools/identcompletiontool.pas	(working copy)
@@ -360,6 +360,9 @@
                                     // property names in source)
     FIDTFoundMethods: TAVLTree;// tree of TCodeTreeNodeExtension Txt=clean text
     FIDTTreeOfUnitFiles: TAVLTree;// tree of TUnitFileInfo
+    FIDTTreeOfUnitFiles_NamespacePath: string;
+    FIDTTreeOfUnitFiles_CaseInsensitive: Boolean;
+    FIDTTreeOfNamespaces: TAVLTree;// tree of TNameSpaceInfo
     procedure AddToTreeOfUnitFileInfo(const AFilename: string);
     procedure AddBaseConstant(const BaseName: PChar);
     procedure AddBaseType(const BaseName: PChar);
@@ -376,7 +379,7 @@
       const Context, GatherContext: TFindContext);
     procedure GatherUsefulIdentifiers(CleanPos: integer;
       const Context, GatherContext: TFindContext);
-    procedure GatherUnitnames;
+    procedure GatherUnitnames(const NameSpacePath: string = '');
     procedure GatherSourceNames(const Context: TFindContext);
     procedure GatherContextKeywords(const Context: TFindContext;
       CleanPos: integer; BeautifyCodeOptions: TBeautifyCodeOptions);
@@ -401,7 +404,8 @@
     function GatherAvailableUnitNames(const CursorPos: TCodeXYPosition;
                              var IdentifierList: TIdentifierList): Boolean;
     function GatherIdentifiers(const CursorPos: TCodeXYPosition;
-                            var IdentifierList: TIdentifierList): boolean;
+                            var IdentifierList: TIdentifierList;
+                            var IdentifierPath: string): boolean;
     function FindCodeContext(const CursorPos: TCodeXYPosition;
                              out CodeContexts: TCodeContextInfo): boolean;
     function FindAbstractMethods(const CursorPos: TCodeXYPosition;
@@ -905,7 +909,8 @@
 
 procedure TIdentCompletionTool.AddToTreeOfUnitFileInfo(const AFilename: string);
 begin
-  AddToTreeOfUnitFiles(FIDTTreeOfUnitFiles,AFilename,false);
+  AddToTreeOfUnitFilesOrNamespaces(FIDTTreeOfUnitFiles,FIDTTreeOfNamespaces,
+    FIDTTreeOfUnitFiles_NamespacePath,AFilename,FIDTTreeOfUnitFiles_CaseInsensitive,false);
 end;
 
 procedure TIdentCompletionTool.AddCompilerProcedure(const AProcName, AParameterList: PChar);
@@ -1225,7 +1230,7 @@
   ctnRecordCase:
     Ident:=@FoundContext.Tool.Src[Params.NewCleanPos];
 
-  ctnUseUnit:
+  ctnUseUnitNamespace,ctnUseUnitClearName:
     if (FoundContext.Tool=Self) then begin
       Ident:=@Src[FoundContext.Node.StartPos];
     end;
@@ -1295,7 +1300,7 @@
         CompilerFuncLevel,
         nil,
         nil,
-        ctnUseUnit);
+        ctnUseUnitClearName);
     CurrentIdentifierList.Add(NewItem);
   end;
 
@@ -1486,7 +1491,7 @@
   end;
 end;
 
-procedure TIdentCompletionTool.GatherUnitnames;
+procedure TIdentCompletionTool.GatherUnitnames(const NameSpacePath: string);
 
   procedure GatherUnitsFromSet;
   begin
@@ -1503,6 +1508,7 @@
   UnitExt: String;
   SrcExt: String;
   CurSourceName: String;
+  NameSpaceInfo: TNameSpaceInfo;
 begin
   UnitPath:='';
   SrcPath:='';
@@ -1510,37 +1516,57 @@
   //DebugLn('TIdentCompletionTool.GatherUnitnames UnitPath="',UnitPath,'" SrcPath="',SrcPath,'"');
   BaseDir:=ExtractFilePath(MainFilename);
   FIDTTreeOfUnitFiles:=nil;
+  FIDTTreeOfNamespaces:=nil;
   try
     // search in unitpath
+    FIDTTreeOfUnitFiles_CaseInsensitive := true;
+    FIDTTreeOfUnitFiles_NamespacePath := NameSpacePath;
     UnitExt:='pp;pas;ppu';
     if Scanner.CompilerMode=cmMacPas then
       UnitExt:=UnitExt+';p';
-    GatherUnitFiles(BaseDir,UnitPath,UnitExt,false,true,FIDTTreeOfUnitFiles);
+    GatherUnitFiles(BaseDir,UnitPath,UnitExt,NameSpacePath,false,true,FIDTTreeOfUnitFiles, FIDTTreeOfNamespaces);
     // search in srcpath
     SrcExt:='pp;pas';
     if Scanner.CompilerMode=cmMacPas then
       SrcExt:=SrcExt+';p';
-    GatherUnitFiles(BaseDir,SrcPath,SrcExt,false,true,FIDTTreeOfUnitFiles);
+    GatherUnitFiles(BaseDir,SrcPath,SrcExt,NameSpacePath,false,true,FIDTTreeOfUnitFiles, FIDTTreeOfNamespaces);
     // add unitlinks
     GatherUnitsFromSet;
     // create list
     CurSourceName:=GetSourceName;
-    ANode:=FIDTTreeOfUnitFiles.FindLowest;
-    while ANode<>nil do begin
-      UnitFileInfo:=TUnitFileInfo(ANode.Data);
-      if CompareIdentifiers(PChar(Pointer(UnitFileInfo.FileUnitName)),
-                            PChar(Pointer(CurSourceName)))<>0
-      then begin
+    if FIDTTreeOfUnitFiles<> nil then
+    begin
+      ANode:=FIDTTreeOfUnitFiles.FindLowest;
+      while ANode<>nil do begin
+        UnitFileInfo:=TUnitFileInfo(ANode.Data);
+        if CompareText(PChar(Pointer(UnitFileInfo.FileUnitName)), Length(UnitFileInfo.FileUnitName),
+                       PChar(Pointer(CurSourceName)), Length(CurSourceName), False)<>0
+        then begin
+          NewItem:=TIdentifierListItem.Create(
+              icompCompatible,true,0,
+              CurrentIdentifierList.CreateIdentifier(UnitFileInfo.FileUnitNameWithoutNamespace),
+              0,nil,nil,ctnUnit);
+          CurrentIdentifierList.Add(NewItem);
+        end;
+        ANode:=FIDTTreeOfUnitFiles.FindSuccessor(ANode);
+      end;
+    end;
+    if FIDTTreeOfNamespaces<>nil then
+    begin
+      ANode:=FIDTTreeOfNamespaces.FindLowest;
+      while ANode<>nil do begin
+        NameSpaceInfo:=TNameSpaceInfo(ANode.Data);
         NewItem:=TIdentifierListItem.Create(
             icompCompatible,true,0,
-            CurrentIdentifierList.CreateIdentifier(UnitFileInfo.FileUnitName),
-            0,nil,nil,ctnUnit);
+            CurrentIdentifierList.CreateIdentifier(NameSpaceInfo.NameSpace),
+            0,nil,nil,ctnUseUnitNamespace);
         CurrentIdentifierList.Add(NewItem);
+        ANode:=FIDTTreeOfNamespaces.FindSuccessor(ANode);
       end;
-      ANode:=FIDTTreeOfUnitFiles.FindSuccessor(ANode);
     end;
   finally
     FreeTreeOfUnitFiles(FIDTTreeOfUnitFiles);
+    FreeTreeOfUnitFiles(FIDTTreeOfNamespaces);
   end;
 end;
 
@@ -2469,8 +2495,8 @@
 end;
 
 function TIdentCompletionTool.GatherIdentifiers(
-  const CursorPos: TCodeXYPosition; var IdentifierList: TIdentifierList
-  ): boolean;
+  const CursorPos: TCodeXYPosition; var IdentifierList: TIdentifierList;
+  var IdentifierPath: string): boolean;
 var
   CleanCursorPos, IdentStartPos, IdentEndPos: integer;
   CursorNode: TCodeTreeNode;
@@ -2544,6 +2570,18 @@
         CurrentIdentifierList.StartAtom:=CurPos;
       end;
 
+      MoveCursorToCleanPos(IdentStartPos);
+      ReadPriorAtom;
+      IdentifierPath := '';
+      while CurPos.Flag = cafPoint do
+      begin
+        ReadPriorAtom;
+        if CurPos.Flag <> cafWord then
+          Break;
+        IdentifierPath := GetUpAtom + '.' + IdentifierPath;
+        ReadPriorAtom;
+      end;
+
       // find context
       {$IFDEF CTDEBUG}
       DebugLn('TIdentCompletionTool.GatherIdentifiers B',
@@ -2553,8 +2591,8 @@
       {$ENDIF}
       GatherContext:=CreateFindContext(Self,CursorNode);
       CurrentIdentifierList.NewMemberVisibility:=GetClassVisibility(CursorNode);
-      if CursorNode.Desc in [ctnUsesSection,ctnUseUnit] then begin
-        GatherUnitNames;
+      if CursorNode.Desc in [ctnUsesSection,ctnUseUnit,ctnUseUnitNamespace,ctnUseUnitClearName] then begin
+        GatherUnitNames(IdentifierPath);
         MoveCursorToCleanPos(IdentEndPos);
         ReadNextAtom;
         if (CurPos.Flag=cafWord) and (not UpAtomIs('IN')) then begin
Index: components/codetools/pascalparsertool.pas
===================================================================
--- components/codetools/pascalparsertool.pas	(revision 50072)
+++ components/codetools/pascalparsertool.pas	(working copy)
@@ -1993,6 +1993,7 @@
 }
 var
   IsUses: Boolean;
+  LastUnitNode: TCodeTreeNode;
 begin
   Result:=false;
   IsUses:=CurNode.Desc=ctnUsesSection;
@@ -2016,8 +2017,14 @@
     CurNode.Desc:=ctnUseUnit;
     repeat
       CurNode.EndPos:=CurPos.EndPos;
+      CreateChildNode;
+      LastUnitNode := CurNode;
+      CurNode.Desc:=ctnUseUnitClearName;
+      CurNode.EndPos:=CurNode.Parent.EndPos;
+      EndChildNode;
       ReadNextAtom;
       if CurPos.Flag<>cafPoint then break;
+      LastUnitNode.Desc:=ctnUseUnitNamespace;
       ReadNextAtom;
       AtomIsIdentifierSaveE;
     until false;
Index: ide/codeexplorer.pas
===================================================================
--- ide/codeexplorer.pas	(revision 50072)
+++ ide/codeexplorer.pas	(working copy)
@@ -933,6 +933,12 @@
       ShowNode:=false;
     end;
 
+    //don't show child nodes of ctnUseUnit
+    if (CodeNode.Desc=ctnUseUnit)
+    then begin
+      ShowChilds:=false;
+    end;
+
     // don't show subs
     if CodeNode.Desc in [ctnConstant,ctnIdentifier,ctnRangedArrayType,
       ctnOpenArrayType,ctnOfConstType,ctnRangeType,ctnTypeType,ctnFileType,
Index: ide/main.pp
===================================================================
--- ide/main.pp	(revision 50072)
+++ ide/main.pp	(working copy)
@@ -9665,6 +9665,7 @@
   ActiveSrcEdit: TSourceEditor;
   ActiveUnitInfo: TUnitInfo;
   LogCaretXY: TPoint;
+  IdentifierPath: string;
 begin
   ActiveSrcEdit:=nil;
   if not BeginCodeTool(ActiveSrcEdit,ActiveUnitInfo,[]) then exit(false);
@@ -9675,7 +9676,8 @@
   {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.DoInitIdentCompletion A');{$ENDIF}
   LogCaretXY:=ActiveSrcEdit.EditorComponent.LogicalCaretXY;
   Result:=CodeToolBoss.GatherIdentifiers(ActiveUnitInfo.Source,
-                                         LogCaretXY.X,LogCaretXY.Y);
+                                         LogCaretXY.X,LogCaretXY.Y, IdentifierPath);
+   SourceEditorManager.DefaultCompletionForm.IdentifierPath := IdentifierPath;
   if not Result then begin
     if JumpToError then
       DoJumpToCodeToolBossError
Index: ide/sourceeditor.pp
===================================================================
--- ide/sourceeditor.pp	(revision 50072)
+++ ide/sourceeditor.pp	(working copy)
@@ -114,6 +114,7 @@
     FActiveEditDefaultBGColor: TColor;
     FActiveEditSelectedFGColor: TColor;
     FActiveEditSelectedBGColor: TColor;
+    FIdentifierPath: string;
 
     procedure ccExecute(Sender: TObject);
     procedure ccCancel(Sender: TObject);
@@ -139,6 +140,7 @@
     CurrentCompletionType: TCompletionType;
     function Manager: TSourceEditorManager;
   public
+    property IdentifierPath: string read FIdentifierPath write FIdentifierPath;
     constructor Create(AOwner: TComponent); override;
     property IdentCompletionJumpToError: Boolean
       read FIdentCompletionJumpToError write FIdentCompletionJumpToError;
@@ -918,6 +920,7 @@
     function  FindIdentCompletionPlugin(SrcEdit: TSourceEditor; JumpToError: boolean;
                                var s: string; var BoxX, BoxY: integer;
                                var UseWordCompletion: boolean): boolean;
+  public
     property DefaultCompletionForm: TSourceEditCompletion
       read GetDefaultCompletionForm;
   public
@@ -2063,7 +2066,7 @@
           CodeToolBoss.IdentifierHistory.Add(
             CodeToolBoss.IdentifierList.FilteredItems[Position]);
           // get value
-          NewValue:=GetIdentCompletionValue(self, KeyChar, ValueType, CursorToLeft);
+          NewValue:=GetIdentCompletionValue(self, KeyChar, IdentifierPath, ValueType, CursorToLeft);
           if ValueType=icvIdentifier then ;
           // insert value plus special chars like brackets, semicolons, ...
           if ValueType <> icvNone then
Index: ide/sourceeditprocs.pas
===================================================================
--- ide/sourceeditprocs.pas	(revision 50072)
+++ ide/sourceeditprocs.pas	(working copy)
@@ -91,6 +91,7 @@
 
 function GetIdentCompletionValue(aCompletion : TSynCompletion;
   AddChar: TUTF8Char;
+  IdentifierPath: string;
   out ValueType: TIdentComplValue; out CursorToLeft: integer): string;
 function BreakLinesInText(const s: string; MaxLineLength: integer): string;
 
@@ -322,12 +323,18 @@
         s:='label';
       end;
 
-    ctnUnit, ctnUseUnit:
+    ctnUnit, ctnUseUnitClearName:
       begin
         AColor:=clBlack;
         s:='unit';
       end;
 
+    ctnUseUnitNamespace:
+      begin
+        AColor:=clBlack;
+        s:='namespace';
+      end;
+
     ctnNone:
       if iliKeyword in IdentItem.Flags then begin
         AColor:=clBlack;
@@ -540,22 +547,31 @@
   end;
 end;
 
-function FindUnitName(IdentList: TIdentifierList;
-  IdentItem: TIdentifierListItem): string;
+function FindUnitOrNameSpaceName(IdentList: TIdentifierList;
+  IdentItem: TIdentifierListItem; IdentifierPath: string): string;
 var
   CodeBuf: TCodeBuffer;
+  LastPointPos: Integer;
 begin
   Result:=IdentItem.Identifier;
-  CodeBuf:=CodeToolBoss.FindUnitSource(IdentList.StartContextPos.Code,Result,'');
-  if CodeBuf=nil then exit;
-  Result:=CodeToolBoss.GetSourceName(CodeBuf,true);
+  CodeBuf:=CodeToolBoss.FindUnitSource(IdentList.StartContextPos.Code,IdentifierPath+Result,'');
+  if CodeBuf=nil then
+    CodeBuf:=CodeToolBoss.FindUnitSource(IdentList.StartContextPos.Code,IdentifierPath+Result,'',True);
+  if CodeBuf=nil then Exit;
+  Result:=Copy(CodeToolBoss.GetSourceName(CodeBuf,true), Length(IdentifierPath)+1, Length(IdentItem.Identifier));
   if Result='' then
-    Result:=IdentItem.Identifier;
+    Result:=IdentItem.Identifier
+  else
+  begin
+    LastPointPos := LastDelimiter('.', Result);
+    if LastPointPos > 0 then
+      Result := Copy(Result, LastPointPos+1, High(Integer));
+  end;
 end;
 
-function GetIdentCompletionValue(aCompletion : TSynCompletion;
-  AddChar: TUTF8Char;
-  out ValueType: TIdentComplValue; out CursorToLeft: integer): string;
+function GetIdentCompletionValue(aCompletion: TSynCompletion;
+  AddChar: TUTF8Char; IdentifierPath: string; out ValueType: TIdentComplValue;
+  out CursorToLeft: integer): string;
 var
   Index: Integer;
   IdentItem: TIdentifierListItem;
@@ -617,7 +633,7 @@
         IsReadOnly:=IdentItem.IsPropertyReadOnly;
       end;
 
-    ctnUnit, ctnPackage, ctnLibrary:
+    ctnUnit, ctnPackage, ctnLibrary, ctnUseUnitNamespace:
       ValueType:=icvUnitName;
   end;
 
@@ -702,7 +718,7 @@
 
     icvUnitName:
       if not IsWordPolicyExcept then
-        Result:=FindUnitName(IdentList,IdentItem);
+        Result:=FindUnitOrNameSpaceName(IdentList,IdentItem,IdentifierPath);
   end;
 
   if CursorAtEnd then ;
@@ -739,7 +755,7 @@
   end;
 
   if CodeToolsOpts.IdentComplAddSemicolon and
-     (IdentItem.GetDesc=ctnUseUnit) and (AddChar<>'.') and
+     (IdentItem.GetDesc in [ctnUseUnitNamespace,ctnUseUnitClearName]) and (AddChar<>'.') and
      not IdentList.StartUpAtomBehindIs('.')//check if there is already a point
   then
     Result+='.';
ct-namespaces-1.patch (40,637 bytes)   

Ondrej Pokorny

2015-10-16 15:46

developer  

namespaces-test.zip (3,089 bytes)

Ondrej Pokorny

2015-10-16 15:57

developer   ~0086620

Patch attached. It seems to work well.

There is one problem though (not connected with my new code). In the program file when I try to complete:

----
program project1;

uses
  MyComp.MyLib.MyUnit1, MyComp.MyLib.|, HelperTest;
----

It works only the first time. The second time it fails because CodeTools fail to find the correct cursor node at |.

It does not happen in a unit:

----
unit MyComp.MyLib.MyUnit2;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, MyComp.MyLib.|;
----

Mattias, could you take a look why CodeTools don't find the correct cursor node?

The problem is in this line in function TIdentCompletionTool.GatherIdentifiers:

    ParseSourceTillCollectionStart(IdentStartXY,CleanCursorPos,CursorNode,
                                   IdentStartPos,IdentEndPos);

It returns the "program" cursor node when it fails.

I wasn't able to fix it unfortunately :(

Anthony Walter

2015-10-16 17:42

reporter   ~0086621

Last edited: 2015-10-16 17:49

View 2 revisions

Ondrej, I tested your patch and it works for me.

http://cache.getlazarus.org/images/lazarus-dotted-namespaces.png

I also noted the problem where the caret (the blinking | is called a caret) is not always placed in the correct position after accepting a name from the code insight list. It's minor problem, but what you've created is a huge improvement.

Thanks!

Ondrej Pokorny

2015-10-16 17:49

developer   ~0086623

Last edited: 2015-10-16 18:19

View 3 revisions

>> I also noted the problem where the caret (the blinking | is called a caret) is not always placed in the correct position after accepting a name from the code insight list.

Do you have a sample code to reproduce it?

Anthony Walter

2015-10-16 17:52

reporter   ~0086624

I'll work on putting together an example. Also note in the screenshot in my last note, the identifiers seem to be in the order listed in the package file. Should this be changed to alphabetical?

Ondrej Pokorny

2015-10-16 17:56

developer   ~0086626

>> Should this be changed to alphabetical?

It is the same behavior as it was before with "normal" files without namespaces. IMO there is an IDE option to sort the identifier list.

Mattias Gaertner

2015-10-27 13:16

manager   ~0086903

About ct-namespaces-1.patch:
Adding nodes for namespaces in uses clause is a good idea. The root node (source node, e.g. ctnUnit) can declare a namespace too, so this should get a sub nodes too (i.e. namepace + identifier).

I don't understand why you must extend GatherIdentifiers with a parameter. Can you explain?

Ondrej Pokorny

2015-10-27 14:40

developer   ~0086904

>> I don't understand why you must extend GatherIdentifiers with a parameter. Can you explain?

Yes, this is not straight-forward.

The IdentifierPath is needed to get the correct case of the added unit/namespace identifier.

See the attached "namespaces-test.zip". The file "mycomp.mylib.myunit1.pas" should always be added as "MyComp.MyLib.MyUnit1" into the uses clause of other units. Without namespace support it was easy to find the unit name. But now, you have to find the correct case for all namespaces in between and also for the last unit name. To achieve it, you need the current namespace path (=identifier path), where the auto completion was started in order to find the target file and read its header.

To reproduce it, open "project1.lpr" and auto-complete "MyComp.MyLib.MyUnit1" in the uses clause. The correct case is always used thanks to the use of IdentifierPath (see sourceeditprocs.pas).

----
How I did it:

I need to set
   SourceEditorManager.DefaultCompletionForm.IdentifierPath := IdentifierPath;
in TMainIDE.DoInitIdentCompletion.

For me the simplest way to get the IdentiefierPath was to add a parameter to CodeToolBoss.GatherIdentifiers / TIdentCompletionTool.GatherIdentifiers.
It could be retrieved with another (new) function as well.

Mattias Gaertner

2015-10-27 15:02

manager   ~0086905

The case is defined in the unit's name. For example "unit MyComp.MyLib.MyUnit1;" in mycomp.mylib.myunit.pas. Right?
Of course parsing all unit headers would be slow.
But we don't need the casing of all available units. Just those shown in the completion box.
Simply add all the unit/namespace identifiers lower case and parse the unit header for the real case in PaintCompletionItem.

Ondrej Pokorny

2015-10-27 16:07

developer   ~0086906

>> The case is defined in the unit's name. For example "unit MyComp.MyLib.MyUnit1;" in mycomp.mylib.myunit.pas. Right?

Yes.

>> Simply add all the unit/namespace identifiers lower case and parse the unit header for the real case in PaintCompletionItem.

In PaintCompletionItem you still need to know what unit to parse. It would probably be also OK to add a custom param to TIdentifierListItem and hand over the unit name to use.

But I don't care about the item in the identifier completion list - the current CodeTools don't do it either and I think it's OK. The important thing is that the right case is used when adding the unit/namespace name into the sources (after applying the indentifier list item by Enter, e.g.). This is also the current way CodeTools work - I didn't change anything about that.

The new thing that is needed is that when a unit with namespaces is added, you need to find the unit to use for the case determination. And for it you need the complete namespace path (identifier path).

On the other hand, in respect of future FPC features it won't be enough (see this one: http://lists.lazarus.freepascal.org/pipermail/lazarus/2015-October/094523.html). So maybe the unit to parse the header should be passed to TIdentifierListItem in any case. Then the IdentifierPath parameter won't be needed.

Ondrej Pokorny

2015-11-10 11:44

developer   ~0087215

@Anthony, caret placed in wrong position: As you haven't uploaded any example and I couldn't reproduce the bug, I am resolving this issue. If your problem still persists, feel free to open another issue report.

Issue History

Date Modified Username Field Change
2012-06-08 20:09 Ludo Brands New Issue
2012-06-08 20:09 Ludo Brands Widgetset => Win32/Win64
2012-06-08 21:53 Mattias Gaertner Status new => assigned
2012-06-08 21:53 Mattias Gaertner Assigned To => Mattias Gaertner
2015-10-16 15:44 Ondrej Pokorny File Added: ct-namespaces-1.patch
2015-10-16 15:46 Ondrej Pokorny File Added: namespaces-test.zip
2015-10-16 15:57 Ondrej Pokorny Note Added: 0086620
2015-10-16 17:42 Anthony Walter Note Added: 0086621
2015-10-16 17:49 Anthony Walter Note Edited: 0086621 View Revisions
2015-10-16 17:49 Ondrej Pokorny Note Added: 0086623
2015-10-16 17:52 Anthony Walter Note Added: 0086624
2015-10-16 17:54 Ondrej Pokorny Note Edited: 0086623 View Revisions
2015-10-16 17:56 Ondrej Pokorny Note Added: 0086626
2015-10-16 18:19 Ondrej Pokorny Note Edited: 0086623 View Revisions
2015-10-27 13:16 Mattias Gaertner Note Added: 0086903
2015-10-27 14:40 Ondrej Pokorny Note Added: 0086904
2015-10-27 15:02 Mattias Gaertner Note Added: 0086905
2015-10-27 16:07 Ondrej Pokorny Note Added: 0086906
2015-11-10 11:39 Ondrej Pokorny Assigned To Mattias Gaertner => Ondrej Pokorny
2015-11-10 11:44 Ondrej Pokorny Fixed in Revision => 50266, 50268
2015-11-10 11:44 Ondrej Pokorny LazTarget => -
2015-11-10 11:44 Ondrej Pokorny Widgetset Win32/Win64 => GTK, GTK 2, Win32/Win64, WinCE, Carbon, Cocoa, QT, fpGUI, CustomDrawn
2015-11-10 11:44 Ondrej Pokorny Note Added: 0087215
2015-11-10 11:44 Ondrej Pokorny Status assigned => resolved
2015-11-10 11:44 Ondrej Pokorny Resolution open => fixed
2015-11-10 11:44 Ondrej Pokorny Target Version => 1.6
2015-11-10 11:50 Ondrej Pokorny Widgetset GTK, GTK 2, Win32/Win64, WinCE, Carbon, Cocoa, QT, fpGUI, CustomDrawn =>
2016-01-23 12:00 Juha Manninen Relationship added related to 0029486
2016-06-27 10:43 Ondrej Pokorny Relationship added related to 0029046