IDE: added quickfix for note local variable not used, remove

git-svn-id: trunk@25498 -
This commit is contained in:
mattias 2010-05-18 14:45:02 +00:00
parent 5cc2b11ac5
commit eaa1ff2fba
6 changed files with 316 additions and 32 deletions

View File

@ -66,8 +66,6 @@ function ExtractCommentContent(const ASource: string; CommentStart: integer;
function FindMainUnitHint(const ASource: string; out Filename: string): boolean;
// indent
procedure GetLineStartEndAtPosition(const Source:string; Position:integer;
var LineStart,LineEnd:integer);
function GetLineIndent(const Source: string; Position: integer): integer;
function GetLineIndentWithTabs(const Source: string; Position: integer;
TabWidth: integer): integer;
@ -93,6 +91,9 @@ function IsValidIdentPair(const NamePair: string;
out First, Second: string): boolean;
// line/code ends
procedure GetLineStartEndAtPosition(const Source:string; Position:integer;
var LineStart,LineEnd:integer);
function GetLineStartPosition(const Source:string; Position:integer): integer;
function LineEndCount(const Txt: string): integer; inline;
function LineEndCount(const Txt: string; out LengthOfLastLine:integer): integer; inline;
function LineEndCount(const Txt: string; StartPos, EndPos: integer;
@ -108,7 +109,8 @@ function FindLineEndOrCodeInFrontOfPosition(const Source: string;
StopAtDirectives: boolean = true; SkipSemicolonComma: boolean = true): integer;
function FindLineEndOrCodeAfterPosition(const Source: string;
Position, MaxPosition: integer; NestedComments: boolean;
StopAtDirectives: boolean = true; SkipEmptyLines: boolean = false): integer;
StopAtDirectives: boolean = true; SkipEmptyLines: boolean = false;
IncludeLineEnd: boolean = false): integer;
function FindFirstLineEndInFrontOfInCode(const Source: string;
Position, MinPosition: integer; NestedComments: boolean): integer;
function FindFirstLineEndAfterInCode(const Source: string;
@ -1332,9 +1334,8 @@ procedure GetLineStartEndAtPosition(const Source:string; Position:integer;
var LineStart,LineEnd:integer);
begin
LineStart:=Position;
while (LineStart>0) and (not (Source[LineStart] in [#10,#13])) do
while (LineStart>1) and (not (Source[LineStart-1] in [#10,#13])) do
dec(LineStart);
inc(LineStart);
LineEnd:=Position;
while (LineEnd<=length(Source)) and (not (Source[LineEnd] in [#10,#13])) do
inc(LineEnd);
@ -2248,10 +2249,10 @@ begin
if (Result>length(Source)) then Result:=length(Source);
if Result=0 then exit;
// search beginning of line
while (Result>1) and (not (Source[Result] in [#10,#13])) do
while (Result>1) and (not (Source[Result-1] in [#10,#13])) do
dec(Result);
// search
while (Result<length(Source)) and (Source[Result]<' ') do inc(Result);
while (Result<=length(Source)) and (Source[Result] in [' ',#9]) do inc(Result);
end;
function GetLineIndent(const Source: string; Position: integer): integer;
@ -2273,7 +2274,8 @@ end;
function FindLineEndOrCodeAfterPosition(const Source: string;
Position, MaxPosition: integer; NestedComments: boolean;
StopAtDirectives: boolean; SkipEmptyLines: boolean): integer;
StopAtDirectives: boolean; SkipEmptyLines: boolean;
IncludeLineEnd: boolean): integer;
{ search forward for a line end or code
ignore line ends in comments
Result is Position of Start of Line End
@ -2284,6 +2286,10 @@ function FindLineEndOrCodeAfterPosition(const Source: string;
1. var i: integer;|#
var j: integer;
If IncludeLineEnd then
var i: integer;|
# var j: integer;
2. var i: integer;| (*
*) #var j: integer;
@ -2293,6 +2299,8 @@ function FindLineEndOrCodeAfterPosition(const Source: string;
#
// comment
var j: integer;
if IncludeLineEnd then the # will be one line below
4. SkipEmptyLines=true
var i: integer;|
@ -2386,6 +2394,13 @@ begin
#10,#13:
begin
if SkipEmptyLines then DoSkipEmptyLines(Result);
if IncludeLineEnd and (Result<=SrcLen) and (Source[Result] in [#10,#13])
then begin
inc(Result);
if (Result<=SrcLen) and (Source[Result] in [#10,#13])
and (Source[Result-1]<>Source[Result]) then
inc(Result);
end;
exit;
end;
#9,' ',';':
@ -3955,6 +3970,13 @@ begin
end;
end;
function GetLineStartPosition(const Source: string; Position: integer): integer;
begin
Result:=Position;
while (Result>1) and (not (Source[Result-1] in [#10,#13])) do
dec(Result);
end;
function LineEndCount(const Txt: string): integer;
var
LengthOfLastLine: integer;

View File

@ -419,7 +419,7 @@ type
function GatherOverloads(Code: TCodeBuffer; X,Y: integer;
out Graph: TDeclarationOverloadsGraph): boolean;
// rename identifier
// rename, remove identifier
function FindReferences(IdentifierCode: TCodeBuffer;
X, Y: integer; TargetCode: TCodeBuffer; SkipComments: boolean;
var ListOfPCodeXYPosition: TFPList): boolean;
@ -429,6 +429,8 @@ type
const OldIdentifier, NewIdentifier: string): boolean;
function ReplaceWord(Code: TCodeBuffer; const OldWord, NewWord: string;
ChangeStrings: boolean): boolean;
function RemoveIdentifierDefinition(Code: TCodeBuffer; X, Y: integer
): boolean; // e.g. remove the variable definition at X,Y
// resourcestring sections
function GatherResourceStringSections(
@ -2323,6 +2325,26 @@ begin
end;
end;
function TCodeToolManager.RemoveIdentifierDefinition(Code: TCodeBuffer; X,
Y: integer): boolean;
var
CursorPos: TCodeXYPosition;
begin
Result:=false;
{$IFDEF CTDEBUG}
DebugLn('TCodeToolManager.RemoveIdentifierDefinition A ',Code.Filename,' X=',X,' Y=',Y);
{$ENDIF}
if not InitCurCodeTool(Code) then exit;
CursorPos.X:=X;
CursorPos.Y:=Y;
CursorPos.Code:=Code;
try
Result:=FCurCodeTool.RemoveIdentifierDefinition(CursorPos,SourceChangeCache);
except
on e: Exception do HandleException(e);
end;
end;
function TCodeToolManager.GatherResourceStringSections(Code: TCodeBuffer;
X, Y: integer; CodePositions: TCodeXYPositions): boolean;
var

View File

@ -211,7 +211,7 @@ type
procedure GetLineInfo(ACleanPos: integer;
out ALineStart, ALineEnd, AFirstAtomStart, ALastAtomEnd: integer);
function FindLineEndOrCodeAfterPosition(StartPos: integer;
SkipEmptyLines: boolean = false): integer;
SkipEmptyLines: boolean = false; IncludeLineEnd: boolean = false): integer;
function FindLineEndOrCodeInFrontOfPosition(StartPos: integer): integer;
function FindLineEndOrCodeInFrontOfPosition(StartPos: integer;
StopAtDirectives: boolean): integer;
@ -2431,7 +2431,7 @@ begin
end;
function TCustomCodeTool.FindLineEndOrCodeAfterPosition(StartPos: integer;
SkipEmptyLines: boolean): integer;
SkipEmptyLines: boolean; IncludeLineEnd: boolean): integer;
{ Searches a nice position in the cleaned source after StartPos.
It will skip any space or comments (not directives) till next
line end or compiler directive or code or include file end.
@ -2443,7 +2443,8 @@ begin
LinkEnd:=Scanner.LinkCleanedEndPos(LinkIndex);
if LinkEnd>StartPos then
Result:=BasicCodeTools.FindLineEndOrCodeAfterPosition(Src,
StartPos,LinkEnd-1,Scanner.NestedComments,true,SkipEmptyLines)
StartPos,LinkEnd-1,Scanner.NestedComments,true,SkipEmptyLines,
IncludeLineEnd)
else
Result:=StartPos;
end;

View File

@ -195,6 +195,10 @@ type
SearchInAncestors: boolean;
out ListOfPInstancePropInfo: TFPList): boolean;
// variables, constants, types
function RemoveIdentifierDefinition(const CursorPos: TCodeXYPosition;
SourceChangeCache: TSourceChangeCache): boolean;
// blocks (e.g. begin..end)
function FindBlockCounterPart(const CursorPos: TCodeXYPosition;
out NewPos: TCodeXYPosition; out NewTopLine: integer): boolean;
@ -5075,6 +5079,109 @@ begin
end;
end;
function TStandardCodeTool.RemoveIdentifierDefinition(
const CursorPos: TCodeXYPosition; SourceChangeCache: TSourceChangeCache
): boolean;
var
CleanCursorPos: integer;
Node: TCodeTreeNode;
PrevSibling: TCodeTreeNode;
NextSibling: TCodeTreeNode;
DeleteStartPos: LongInt;
DeleteEndPos: LongInt;
DeleteFirstTokenOfLine: Boolean;
begin
Result:=false;
BuildTreeAndGetCleanPos(trAll,CursorPos,CleanCursorPos,[]);
Node:=BuildSubTreeAndFindDeepestNodeAtPos(CleanCursorPos,true);
if Node.Desc in AllIdentifierDefinitions then begin
// Examples:
// var i, X: integer; -> var i[, X]: integer;
// var i, X, j: integer; -> var i, [X, ]j: integer;
// var X, i: integer; -> var [X, ]i: integer;
// type X = integer;
// const X = 0;
// const X : integer = 0;
PrevSibling:=nil;
NextSibling:=nil;
if (Node.PriorBrother<>nil) and (Node.PriorBrother.FirstChild=nil) then
PrevSibling:=Node.PriorBrother;
if (Node.FirstChild=nil) and (Node.NextBrother<>nil) then
NextSibling:=Node.NextBrother;
DeleteStartPos:=Node.StartPos;
DeleteEndPos:=Node.StartPos+GetIdentLen(@Src[Node.StartPos]);
if NextSibling<>nil then begin
// var i, X, j: integer; -> var i, [X, ]j: integer;
// var X, i: integer; -> var [X, ]i: integer;
MoveCursorToCleanPos(Node.StartPos);
ReadNextAtom;
AtomIsIdentifier(true);
if not ReadNextAtomIsChar(',') then RaiseCharExpectedButAtomFound(',');
DeleteEndPos:=CurPos.EndPos;
end else if PrevSibling<>nil then begin
// var i, X: integer; -> var i[, X]: integer;
MoveCursorToCleanPos(PrevSibling.StartPos);
ReadNextAtom;
AtomIsIdentifier(true);
if not ReadNextAtomIsChar(',') then RaiseCharExpectedButAtomFound(',');
DeleteStartPos:=CurPos.StartPos;
end else begin
// delete whole declaration
if (Node.Parent.Desc in AllDefinitionSections)
and (Node.PriorBrother=nil) and (Node.NextBrother=nil) then begin
// delete whole section
DeleteStartPos:=Node.Parent.StartPos;
DeleteEndPos:=Node.Parent.EndPos;
end else if Node.Parent.Desc=ctnParameterList then begin
// delete whole parameter including modifier, type and default value
if Node.PriorBrother<>nil then begin
// ... var i: integer; var X: ... -> ... var i: integer[; var X: ...
MoveCursorToCleanPos(Node.PriorBrother.EndPos);
repeat
ReadNextAtom;
if CurPos.Flag=cafSemicolon then begin
DeleteStartPos:=CurPos.EndPos;
break;
end;
until CurPos.StartPos>=Node.StartPos;
end else begin
// (var X: ... -> ([; X: ...
MoveCursorToCleanPos(Node.Parent.StartPos);
ReadNextAtom;
if CurPos.Flag in [cafRoundBracketOpen,cafEdgedBracketOpen] then
DeleteStartPos:=CurPos.EndPos;
end;
if Node.NextBrother<>nil then begin
// ... var X: integer; var i: ... -> .. var X: integer;] var i: ...
DeleteEndPos:=Node.PriorBrother.EndPos;
end else begin
// ... var X: integer) -> .. var X: integer])
DeleteEndPos:=Node.EndPos;
end;
end else begin
// keep section, delete whole declaration
DeleteEndPos:=Node.EndPos;
end;
end;
// include corresponding comments
DeleteFirstTokenOfLine:=FindFirstNonSpaceCharInLine(Src,DeleteStartPos)=DeleteStartPos;
//DebugLn(['TStandardCodeTool.RemoveIdentifierDefinition ',dbgstr(copy(Src,FindFirstNonSpaceCharInLine(Src,DeleteStartPos),10))]);
DeleteEndPos:=FindLineEndOrCodeAfterPosition(DeleteEndPos,true,DeleteFirstTokenOfLine);
if DeleteFirstTokenOfLine and (Src[DeleteEndPos-1] in [#10,#13]) then begin
// delete first and last token of line
// => remove the entire line
DeleteStartPos:=GetLineStartPosition(Src,DeleteStartPos);
end;
//DebugLn(['TStandardCodeTool.RemoveIdentifierDefinition "',dbgstr(copy(Src,DeleteStartPos,DeleteEndPos-DeleteStartPos)),'" IncludeLineEnd=',DeleteFirstTokenOfLine]);
// delete
SourceChangeCache.MainScanner:=Scanner;
if not SourceChangeCache.Replace(gtNone,gtNone,DeleteStartPos,DeleteEndPos,'')
then exit;
Result:=SourceChangeCache.Apply;
end;
end;
function TStandardCodeTool.FindBlockCounterPart(
const CursorPos: TCodeXYPosition;
out NewPos: TCodeXYPosition; out NewTopLine: integer): boolean;

View File

@ -4774,6 +4774,8 @@ resourcestring
+'filename, the hostname with an optional username and the filename of '
+'gdb on the remote computer. For example: %s/usr/bin/ssh username@'
+'hostname gdb%s or: %s/usr/bin/setsid /usr/bin/ssh username@hostname gdb%s';
lisRemoveUnitFromUsesSection = 'Remove unit from uses section';
lisRemoveLocalVariable = 'Remove local variable %s';
implementation

View File

@ -83,6 +83,15 @@ type
procedure Execute(const Msg: TIDEMessageLine; Step: TIMQuickFixStep); override;
end;
{ TQuickFixLocalVariableNotUsed_Remove }
TQuickFixLocalVariableNotUsed_Remove = class(TIDEMsgQuickFixItem)
public
constructor Create;
function IsApplicable(Line: TIDEMessageLine): boolean; override;
procedure Execute(const Msg: TIDEMessageLine; Step: TIMQuickFixStep); override;
end;
procedure QuickFixParameterNotUsed(Sender: TObject; Step: TIMQuickFixStep;
Msg: TIDEMessageLine);
procedure QuickFixUnitNotUsed(Sender: TObject; Step: TIMQuickFixStep;
@ -96,6 +105,37 @@ procedure FreeStandardIDEQuickFixItems;
implementation
procedure ShowError(Msg: string);
begin
MessageDlg('QuickFix error',Msg,mtError,[mbCancel],0);
end;
function IsIdentifierInCode(Code: TCodeBuffer; X,Y: integer;
Identifier, ErrorMsg: string): boolean;
var
p: integer;
IdentStart: integer;
IdentEnd: integer;
begin
Result:=false;
if Code=nil then begin
ShowError(ErrorMsg+' (Code=nil)');
exit;
end;
Code.LineColToPosition(Y,X,p);
if p<1 then begin
ShowError(ErrorMsg+' (position outside of source');
exit;
end;
GetIdentStartEndAtPosition(Code.Source,p,IdentStart,IdentEnd);
if SysUtils.CompareText(Identifier,copy(Code.Source,IdentStart,IdentEnd-IdentStart))<>0
then begin
ShowError(ErrorMsg);
exit;
end;
Result:=true;
end;
procedure QuickFixParameterNotUsed(Sender: TObject; Step: TIMQuickFixStep;
Msg: TIDEMessageLine);
begin
@ -114,6 +154,7 @@ begin
if not REMatches(Msg.Msg,'Unit "([a-z_0-9]+)" not used','I') then begin
DebugLn('QuickFixUnitNotUsed invalid message ',Msg.Msg);
ShowError('QuickFix: UnitNotUsed invalid message '+Msg.Msg);
exit;
end;
UnneededUnitname:=REVar(1);
@ -181,6 +222,7 @@ begin
RegisterIDEMsgQuickFix(TQuickFixLinkerUndefinedReference.Create);
RegisterIDEMsgQuickFix(TQuickFixClassWithAbstractMethods.Create);
RegisterIDEMsgQuickFix(TQuickFixIdentifierNotFoundAddLocal.Create);
RegisterIDEMsgQuickFix(TQuickFixLocalVariableNotUsed_Remove.Create);
end;
procedure FreeStandardIDEQuickFixItems;
@ -219,6 +261,7 @@ begin
if not REMatches(Msg.Msg,'Can''t find unit ([a-z_0-9]+)','I') then begin
DebugLn('QuickFixUnitNotFoundPosition invalid message ',Msg.Msg);
ShowError('QuickFix: UnitNotFoundPosition invalid message '+Msg.Msg);
exit;
end;
MissingUnitname:=REVar(1);
@ -230,11 +273,13 @@ begin
NewFilename:=LazarusIDE.FindUnitFile(UsedByUnit);
if NewFilename='' then begin
DebugLn('QuickFixUnitNotFoundPosition unit not found: ',UsedByUnit);
ShowError('QuickFix: UnitNotFoundPosition unit not found: '+UsedByUnit);
exit;
end;
CodeBuf:=CodeToolBoss.LoadFile(NewFilename,false,false);
if CodeBuf=nil then begin
DebugLn('QuickFixUnitNotFoundPosition unable to load unit: ',NewFilename);
ShowError('QuickFix: UnitNotFoundPosition unable to load unit: '+NewFilename);
exit;
end;
end;
@ -244,6 +289,7 @@ begin
NamePos,InPos)
then begin
DebugLn('QuickFixUnitNotFoundPosition failed due to syntax errors or '+MissingUnitname+' is not used in '+CodeBuf.Filename);
LazarusIDE.DoJumpToCodeToolBossError;
exit;
end;
if InPos=0 then ;
@ -277,13 +323,6 @@ procedure TQuickFixLinkerUndefinedReference.Execute(const Msg: TIDEMessageLine;
unit1.o(.text+0x3a):unit1.pas:48: undefined reference to `DoesNotExist'
}
procedure Error(const Msg: string);
begin
DebugLn('TQuickFixLinkerUndefinedReference.Execute ',Msg);
MessageDlg('TQuickFixLinkerUndefinedReference.Execute',
Msg,mtError,[mbCancel],0);
end;
procedure JumpTo(Line1, Line2: TIDEMessageLine);
var
Identifier: String;
@ -343,26 +382,22 @@ procedure TQuickFixLinkerUndefinedReference.Execute(const Msg: TIDEMessageLine;
DebugLn(['TQuickFixLinkerUndefinedReference.JumpTo Filename="',Filename,'" MangledFunction="',MangledFunction,'" Identifier="',Identifier,'" SourceFilename="',SourceFilename,'" SourceLine=',SourceLine]);
CurProject:=LazarusIDE.ActiveProject;
if CurProject=nil then begin
Error('no project');
ShowError('QuickFix: LinkerUndefinedReference no project');
exit;
end;
if (CurProject.MainFile=nil) then begin
Error('no main file in project');
exit;
end;
if (CurProject.MainFile=nil) then begin
Error('no main file in project');
ShowError('QuickFix: LinkerUndefinedReference no main file in project');
exit;
end;
CodeBuf:=CodeToolBoss.LoadFile(CurProject.MainFile.Filename,true,false);
if (CodeBuf=nil) then begin
Error('project main file has no source');
ShowError('QuickFix: LinkerUndefinedReference project main file has no source');
exit;
end;
AnUnitName:=ExtractFilenameOnly(Filename);
CodeBuf:=CodeToolBoss.FindUnitSource(CodeBuf,AnUnitName,'');
if (CodeBuf=nil) then begin
Error('unit not found: '+AnUnitName);
ShowError('QuickFix: LinkerUndefinedReference unit not found: '+AnUnitName);
exit;
end;
if not CodeToolBoss.JumpToLinkerIdentifier(CodeBuf,
@ -372,7 +407,7 @@ procedure TQuickFixLinkerUndefinedReference.Execute(const Msg: TIDEMessageLine;
if CodeToolBoss.ErrorCode<>nil then
LazarusIDE.DoJumpToCodeToolBossError
else
Error('function not found: '+MangledFunction+' Identifier='+Identifier);
ShowError('QuickFix: LinkerUndefinedReference function not found: '+MangledFunction+' Identifier='+Identifier);
exit;
end;
LazarusIDE.DoOpenFileAndJumpToPos(NewCode.Filename,Point(NewX,NewY),
@ -438,10 +473,11 @@ begin
// get class name
if not REMatches(Msg.Msg,'Warning: Constructing a class "([a-z_0-9]+)"','I') then begin
DebugLn('QuickFixClassWithAbstractMethods invalid message ',Msg.Msg);
ShowError('QuickFix: ClassWithAbstractMethods invalid message '+Msg.Msg);
exit;
end;
CurClassName:=REVar(1);
DebugLn(['TQuickFixClassWithAbstractMethods.Execute Class=',CurClassName]);
//DebugLn(['TQuickFixClassWithAbstractMethods.Execute Class=',CurClassName]);
// find the class
@ -449,6 +485,7 @@ begin
CodeToolBoss.Explore(CodeBuf,Tool,false,true);
if Tool=nil then begin
DebugLn(['TQuickFixClassWithAbstractMethods.Execute no tool for ',CodeBuf.Filename]);
ShowError('QuickFix: ClassWithAbstractMethods no tool for '+CodeBuf.Filename);
exit;
end;
@ -465,12 +502,13 @@ begin
end;
exit;
end;
DebugLn(['TQuickFixClassWithAbstractMethods.Execute Declaration at ',NewCode.Filename,' ',NewX,',',NewY]);
//DebugLn(['TQuickFixClassWithAbstractMethods.Execute Declaration at ',NewCode.Filename,' ',NewX,',',NewY]);
if LazarusIDE.DoOpenFileAndJumpToPos(NewCode.Filename,
Point(NewX,NewY),NewTopLine,-1,-1,[])<>mrOk
then begin
DebugLn(['TQuickFixClassWithAbstractMethods.Execute failed opening ',NewCode.Filename]);
ShowError('QuickFix: ClassWithAbstractMethods failed opening '+NewCode.Filename);
exit;
end;
@ -555,10 +593,17 @@ begin
// get identifier
if not REMatches(Msg.Msg,'Error: Identifier not found "([a-z_0-9]+)"','I') then begin
DebugLn('TQuickFixIdentifierNotFoundAddLocal invalid message ',Msg.Msg);
ShowError('QuickFix: IdentifierNotFoundAddLocal invalid message '+Msg.Msg);
exit;
end;
Identifier:=REVar(1);
DebugLn(['TQuickFixIdentifierNotFoundAddLocal.Execute Identifier=',Identifier]);
//DebugLn(['TQuickFixIdentifierNotFoundAddLocal.Execute Identifier=',Identifier]);
if not IsIdentifierInCode(CodeBuf,Caret.X,Caret.Y,Identifier,
Identifier+' not found in '+CodeBuf.Filename
+' at line '+IntToStr(Caret.Y)+', column '+IntToStr(Caret.X)+'.'#13
+'Maybe the message is outdated.')
then exit;
if not CodeToolBoss.CreateVariableForIdentifier(CodeBuf,Caret.X,Caret.Y,-1,
NewCode,NewX,NewY,NewTopLine)
@ -577,7 +622,7 @@ end;
constructor TQuickFixUnitNotFound_Remove.Create;
begin
Name:='Search unit: Error: Can''t find unit Name';
Caption:='Remove unit from uses section';
Caption:=lisRemoveUnitFromUsesSection;
Steps:=[imqfoMenuItem];
end;
@ -618,6 +663,7 @@ begin
// get unitname
if not REMatches(Msg.Msg,'Fatal: Can''t find unit ([a-z_0-9]+) ','I') then begin
DebugLn('TQuickFixUnitNotFound_Remove invalid message ',Msg.Msg);
ShowError('QuickFix: UnitNotFound_Remove invalid message '+Msg.Msg);
exit;
end;
AnUnitName:=REVar(1);
@ -625,6 +671,7 @@ begin
if (AnUnitName='') or (not IsValidIdent(AnUnitName)) then begin
DebugLn(['TQuickFixUnitNotFound_Remove.Execute not an identifier "',dbgstr(AnUnitName),'"']);
ShowError('QuickFix: UnitNotFound_Remove not an identifier "'+dbgstr(AnUnitName)+'"');
exit;
end;
@ -640,5 +687,88 @@ begin
end;
end;
{ TQuickFixLocalVariableNotUsed_Remove }
constructor TQuickFixLocalVariableNotUsed_Remove.Create;
begin
Name:='Remove local variable: Note: Local variable "x" not used';
Caption:='Remove local variable';
Steps:=[imqfoMenuItem];
end;
function TQuickFixLocalVariableNotUsed_Remove.IsApplicable(Line: TIDEMessageLine
): boolean;
const
SearchStr1 = ') Note: Local variable "';
SearchStr2 = '" not used';
var
Msg: String;
StartPos: integer;
p: LongInt;
Variable: String;
begin
Result:=false;
if (Line.Parts=nil) then exit;
Msg:=Line.Msg;
//DebugLn(['TQuickFixLocalVariableNotUsed_Remove.IsApplicable Msg="',Msg,'" ',System.Pos(SearchStr1,Msg),' ',System.Pos(SearchStr2,Msg)]);
StartPos:=System.Pos(SearchStr1,Msg);
if StartPos<1 then exit;
inc(StartPos,length(SearchStr1));
p:=StartPos;
while (p<=length(Msg)) and (Msg[p]<>'"') do inc(p);
if copy(Msg,p,length(SearchStr2))<>SearchStr2 then exit;
Variable:=copy(Msg,StartPos,p-StartPos);
if (Variable='') or not IsValidIdent(Variable) then exit;
Caption:=Format(lisRemoveLocalVariable, [Variable]);
Result:=true;
end;
procedure TQuickFixLocalVariableNotUsed_Remove.Execute(
const Msg: TIDEMessageLine; Step: TIMQuickFixStep);
var
CodeBuf: TCodeBuffer;
Filename: string;
Caret: TPoint;
Variable: String;
begin
if Step=imqfoMenuItem then begin
DebugLn(['TQuickFixLocalVariableNotUsed_Remove.Execute ']);
// get source position
if not GetMsgLineFilename(Msg,CodeBuf) then exit;
Msg.GetSourcePosition(Filename,Caret.Y,Caret.X);
if not LazarusIDE.BeginCodeTools then begin
DebugLn(['TQuickFixLocalVariableNotUsed_Remove.Execute failed because IDE busy']);
exit;
end;
// get variables name
if not REMatches(Msg.Msg,'Note: Local variable "([a-z_0-9]+)" not used','I')
then begin
DebugLn('TQuickFixLocalVariableNotUsed_Remove invalid message ',Msg.Msg);
ShowError('QuickFix: LocalVariableNotUsed_Remove invalid message '+Msg.Msg);
exit;
end;
Variable:=REVar(1);
//DebugLn(['TQuickFixLocalVariableNotUsed_Remove.Execute Variable=',Variable]);
// check if the variable is at that position
if not IsIdentifierInCode(CodeBuf,Caret.X,Caret.Y,Variable,
Variable+' not found in '+CodeBuf.Filename
+' at line '+IntToStr(Caret.Y)+', column '+IntToStr(Caret.X)+'.'#13
+'Maybe the message is outdated.')
then exit;
if not CodeToolBoss.RemoveIdentifierDefinition(CodeBuf,Caret.X,Caret.Y) then
begin
DebugLn(['TQuickFixLocalVariableNotUsed_Remove.Execute remove failed']);
LazarusIDE.DoJumpToCodeToolBossError;
exit;
end;
// message fixed -> clean
Msg.Msg:='';
end;
end;
end.