core/src/line_completer.cpp
changeset 0 7f656887cf89
child 95 b3ffff030d5c
equal deleted inserted replaced
-1:000000000000 0:7f656887cf89
       
     1 // line_completer.cpp
       
     2 // 
       
     3 // Copyright (c) 2006 - 2010 Accenture. All rights reserved.
       
     4 // This component and the accompanying materials are made available
       
     5 // under the terms of the "Eclipse Public License v1.0"
       
     6 // which accompanies this distribution, and is available
       
     7 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 // 
       
     9 // Initial Contributors:
       
    10 // Accenture - Initial contribution
       
    11 //
       
    12 
       
    13 #include <f32file.h>
       
    14 #include <fshell/ltkutils.h>
       
    15 #include "lexer.h"
       
    16 #include "command_factory.h"
       
    17 #include "line_completer.h"
       
    18 
       
    19 _LIT(KTab, "\t");
       
    20 _LIT(KWild, "*");
       
    21 _LIT(KDoubleQuote, "\"");
       
    22 _LIT(KSpace, " ");
       
    23 _LIT(KFileNameSlash, "\\");
       
    24 _LIT(KPipe, "|");
       
    25 _LIT(KDoublePipe, "||");
       
    26 _LIT(KAmpersand, "&");
       
    27 _LIT(KDoubleAmpersand, "&&");
       
    28 _LIT(KAmpersandPipe, "&|");
       
    29 _LIT(KSemicolon, ";");
       
    30 _LIT(KDollar, "$");
       
    31 _LIT(KDashDash, "--");
       
    32 _LIT(KDash, "-");
       
    33 
       
    34 CLineCompleter* CLineCompleter::NewL(RFs& aFs, CCommandFactory& aCommandFactory, IoUtils::CEnvironment& aEnv)
       
    35 	{
       
    36 	CLineCompleter* self = new(ELeave) CLineCompleter(aFs, aCommandFactory, aEnv);
       
    37 	CleanupStack::PushL(self);
       
    38 	self->ConstructL();
       
    39 	CleanupStack::Pop(self);
       
    40 	return self;
       
    41 	}
       
    42 
       
    43 CLineCompleter::~CLineCompleter()
       
    44 	{
       
    45 	delete iLexer;
       
    46 	delete iLastUsedCif;
       
    47 	}
       
    48 
       
    49 void CLineCompleter::LcCompleteLineL(TConsoleLine& aLine, const TChar& aEscapeChar)
       
    50 	{
       
    51 	iLexer->Set(aLine.ContentsToCursor(), aEscapeChar);
       
    52 	TToken token(iLexer->NextToken());
       
    53 	TToken firstToken = token;
       
    54 	TToken prevToken(TToken::EString, KNullDesC, aLine.ContentsToCursor().Length());
       
    55 	TInt lastTokenIdx = 0;
       
    56 	while (iLexer->More())
       
    57 		{
       
    58 		// More than one token - skip to last.
       
    59 		prevToken = token;
       
    60 		token = iLexer->NextToken();
       
    61 		lastTokenIdx++;
       
    62 		}
       
    63 
       
    64 	switch (prevToken.Type())
       
    65 		{
       
    66 		case TToken::EPipe:
       
    67 		case TToken::EAmpersand:
       
    68 		case TToken::ESemicolon:
       
    69 		case TToken::EDoublePipe:
       
    70 		case TToken::EDoubleAmpersand:
       
    71 		case TToken::EAmpersandPipe:
       
    72 			CompleteCommandNameL(aLine, token, aEscapeChar);
       
    73 			break;
       
    74 		default:
       
    75 			if (token.Position() + token.String().Length() < aLine.ContentsToCursor().Length())
       
    76 				{
       
    77 				// Looks like there's whitespace after this token, so consider it to be completing the empty token.
       
    78 				token = TToken(TToken::EString, KNullDesC, aLine.ContentsToCursor().Length());
       
    79 				}
       
    80 			else if (lastTokenIdx == 0)
       
    81 				{
       
    82 				CompleteCommandNameL(aLine, token, aEscapeChar);
       
    83 				break;
       
    84 				}
       
    85 
       
    86 			if (token.String().Left(1) == KDollar)
       
    87 				{
       
    88 				CompleteEnvVarL(aLine, token, aEscapeChar);
       
    89 				}
       
    90 			else if (token.String() == KDash || token.String().Left(2) == KDashDash)
       
    91 				{
       
    92 				// Check the CIF is loaded and it's the right one
       
    93 				if (iLastUsedCif && iLastUsedCif->Name().CompareF(firstToken.String()) != 0)
       
    94 					{
       
    95 					delete iLastUsedCif;
       
    96 					iLastUsedCif = NULL;
       
    97 					}
       
    98 				TInt err = KErrNone;
       
    99 				if (iLastUsedCif == NULL)
       
   100 					{
       
   101 					// We need to supress StaticLeaveIfErrs from CCommandInfoFile::NewL causing a PrintError if there's no CIF for this command,
       
   102 					// hence the use of TRAP_QUIETLY.
       
   103 					TRAP_QUIETLY(err, iLastUsedCif = IoUtils::CCommandInfoFile::NewL(iFs, iEnv, firstToken.String()));
       
   104 					}
       
   105 				if (err == KErrNone)
       
   106 					{
       
   107 					CompleteOptionL(aLine, token, aEscapeChar);
       
   108 					}
       
   109 				}
       
   110 			else
       
   111 				{
       
   112 				CompleteFileNameL(aLine, token, aEscapeChar);
       
   113 				}
       
   114 			break;
       
   115 		}
       
   116 	}
       
   117 
       
   118 CLineCompleter::CLineCompleter(RFs& aFs, CCommandFactory& aCommandFactory, IoUtils::CEnvironment& aEnv)
       
   119 	: iFs(aFs), iCommandFactory(aCommandFactory), iEnv(aEnv)
       
   120 	{
       
   121 	}
       
   122 
       
   123 void CLineCompleter::ConstructL()
       
   124 	{
       
   125 	iLexer = CLexer::NewL();
       
   126 	iLexer->DefineTokenTypeL(TToken::EPipe, KPipe);
       
   127 	iLexer->DefineTokenTypeL(TToken::EAmpersand, KAmpersand);
       
   128 	iLexer->DefineTokenTypeL(TToken::ESemicolon, KSemicolon);
       
   129 	iLexer->DefineTokenTypeL(TToken::EDoublePipe, KDoublePipe);
       
   130 	iLexer->DefineTokenTypeL(TToken::EDoubleAmpersand, KDoubleAmpersand);
       
   131 	iLexer->DefineTokenTypeL(TToken::EAmpersandPipe, KAmpersandPipe);
       
   132 	}
       
   133 
       
   134 HBufC* SimplifyStringLC(const TDesC& aString, const TChar& aEscapeChar)
       
   135 	{
       
   136 	HBufC* buf = HBufC::NewLC(aString.Length());
       
   137 	TPtr bufPtr(buf->Des());
       
   138 	TLex lex(aString);
       
   139 	while (!lex.Eos())
       
   140 		{
       
   141 		TChar c = lex.Get();
       
   142 		if (c == aEscapeChar)
       
   143 			{
       
   144 			if (!lex.Eos())
       
   145 				{
       
   146 				bufPtr.Append(lex.Get());
       
   147 				}
       
   148 			}
       
   149 		else if (c == '\'')
       
   150 			{
       
   151 			while (!lex.Eos())
       
   152 				{
       
   153 				c = lex.Get();
       
   154 				if (c == '\'')
       
   155 					{
       
   156 					break;
       
   157 					}
       
   158 				else
       
   159 					{
       
   160 					bufPtr.Append(c);
       
   161 					}
       
   162 				}
       
   163 			}
       
   164 		else if (c == '"')
       
   165 			{
       
   166 			while (!lex.Eos())
       
   167 				{
       
   168 				c = lex.Get();
       
   169 				if (c == aEscapeChar)
       
   170 					{
       
   171 					bufPtr.Append(lex.Get());
       
   172 					}
       
   173 				else if (c == '"')
       
   174 					{
       
   175 					break;
       
   176 					}
       
   177 				else
       
   178 					{
       
   179 					bufPtr.Append(c);
       
   180 					}
       
   181 				}
       
   182 			}
       
   183 		else
       
   184 			{
       
   185 			bufPtr.Append(c);
       
   186 			}
       
   187 		}
       
   188 	return buf;
       
   189 	}
       
   190 
       
   191 void CLineCompleter::CompleteCommandNameL(TConsoleLine& aLine, const TToken& aToken, const TChar& aEscapeChar) const
       
   192 	{
       
   193 	RArray<TPtrC> commands;
       
   194 	CleanupClosePushL(commands);
       
   195 	iCommandFactory.ListCommandsL(commands);
       
   196 	HBufC* commandBuf = SimplifyStringLC(aToken.String(), aEscapeChar);
       
   197 
       
   198 	// Remove commands that don't match aLine.
       
   199 	TInt i;
       
   200 	for (i = (commands.Count() - 1); i >= 0; --i)
       
   201 		{
       
   202 		if (commands[i].Find(*commandBuf) != 0)
       
   203 			{
       
   204 			commands.Remove(i);
       
   205 			}
       
   206 		}
       
   207 
       
   208 	if (commands.Count() > 0)
       
   209 		{
       
   210 		CompleteL(aLine, aToken, commands, NULL, NULL);
       
   211 		}
       
   212 	else
       
   213 		{
       
   214 		CompleteFileNameL(aLine, aToken, aEscapeChar);
       
   215 		}
       
   216 	CleanupStack::PopAndDestroy(2, &commands);
       
   217 	}
       
   218 
       
   219 void RemovePartialElement(HBufC*& aBuf)
       
   220 	{
       
   221 	TPtr ptr(aBuf->Des());
       
   222 	while (ptr.Length() > 0)
       
   223 		{
       
   224 		if (ptr[ptr.Length() - 1] == KPathDelimiter)
       
   225 			{
       
   226 			break;
       
   227 			}
       
   228 		else
       
   229 			{
       
   230 			ptr.Delete(ptr.Length() - 1, 1);
       
   231 			}
       
   232 		}
       
   233 	}
       
   234 
       
   235 void CLineCompleter::CompleteFileNameL(TConsoleLine& aLine, const TToken& aToken, const TChar& aEscapeChar) const
       
   236 	{
       
   237 	HBufC* fileNameBuf = SimplifyStringLC(aToken.String(), aEscapeChar);
       
   238 	IoUtils::TFileName2 absFileName(*fileNameBuf);
       
   239 	RemovePartialElement(fileNameBuf);
       
   240 	absFileName.Append(KWild);
       
   241 	absFileName.MakeAbsoluteL(iEnv.Pwd());
       
   242 	CDir* files;
       
   243 	User::LeaveIfError(iFs.GetDir(absFileName, KEntryAttNormal | KEntryAttDir, ESortByName, files));
       
   244 	CleanupStack::PushL(files);
       
   245 	RArray<TPtrC> fileNames;
       
   246 	CleanupClosePushL(fileNames);
       
   247 	const TInt numFiles = files->Count();
       
   248 	if (numFiles == 1)
       
   249 		{
       
   250 		absFileName = absFileName.DriveAndPath();
       
   251 		absFileName.AppendComponentL((*files)[0].iName);
       
   252 		TEntry entry;
       
   253 		User::LeaveIfError(iFs.Entry(absFileName, entry));
       
   254 		User::LeaveIfError(fileNames.Append(absFileName.NameAndExt()));
       
   255 		CompleteL(aLine, aToken, fileNames, fileNameBuf, entry.IsDir() ? &KFileNameSlash : NULL);
       
   256 		}
       
   257 	else if (numFiles > 1)
       
   258 		{
       
   259 		for (TInt i = 0; i < numFiles; ++i)
       
   260 			{
       
   261 			User::LeaveIfError(fileNames.Append(TPtrC((*files)[i].iName)));
       
   262 			}
       
   263 		CompleteL(aLine, aToken, fileNames, fileNameBuf, NULL);
       
   264 		}
       
   265 	CleanupStack::PopAndDestroy(3, fileNameBuf);
       
   266 	}
       
   267 
       
   268 TBool NeedsQuoting(const TDesC& aDes)
       
   269 	{
       
   270 	return ((aDes.Locate(TChar(' ')) >= 0) || aDes.Locate(TChar('\'')) >= 0);
       
   271 	}
       
   272 
       
   273 void CLineCompleter::CompleteL(TConsoleLine& aLine, const TToken& aToken, const RArray<TPtrC> aPossibilities, const TDesC* aPrefix, const TDesC* aSuffix, TBool aPrefixIsPartOfToken) const
       
   274 	{
       
   275 	const TInt numPossibilities = aPossibilities.Count();
       
   276 
       
   277 	if (numPossibilities > 1)
       
   278 		{
       
   279 		// Fill out possibilities buffer.
       
   280 		IoUtils::CTextBuffer* possibilities = IoUtils::CTextBuffer::NewLC(0x100);
       
   281 		for (TInt i = 0; i < numPossibilities; ++i)
       
   282 			{
       
   283 			if (aPrefixIsPartOfToken) possibilities->AppendL(*aPrefix);
       
   284 			possibilities->AppendL(aPossibilities[i]);
       
   285 			if (i != (numPossibilities - 1))
       
   286 				{
       
   287 				possibilities->AppendL(KTab);
       
   288 				}
       
   289 			}
       
   290 
       
   291 		aLine.PrintCompletionPossibilitiesL(possibilities->Descriptor());
       
   292 		CleanupStack::PopAndDestroy(possibilities);
       
   293 		}
       
   294 
       
   295 	if (numPossibilities > 0)
       
   296 		{
       
   297 		IoUtils::CTextBuffer* completion = IoUtils::CTextBuffer::NewLC(0x100);
       
   298 		TPtrC commonChars(NULL, 0);
       
   299 		if (numPossibilities > 1)
       
   300 			{
       
   301 			// Find common leading characters of the possibilities.
       
   302 			TInt commonCharPos = -1;
       
   303 			TBool finished(EFalse);
       
   304 			do
       
   305 				{
       
   306 				++commonCharPos;
       
   307 				TChar character(0);
       
   308 				for (TInt i = 0; i < numPossibilities; ++i)
       
   309 					{
       
   310 					if (commonCharPos >= aPossibilities[i].Length())
       
   311 						{
       
   312 						finished = ETrue;
       
   313 						break;
       
   314 						}
       
   315 					else if (i == 0)
       
   316 						{
       
   317 						character = aPossibilities[0][commonCharPos];
       
   318 						character.Fold();
       
   319 						}
       
   320 					else
       
   321 						{
       
   322 						TChar c(aPossibilities[i][commonCharPos]);
       
   323 						c.Fold();
       
   324 						if (c != character)
       
   325 							{
       
   326 							finished = ETrue;
       
   327 							break;
       
   328 							}
       
   329 						}
       
   330 					}
       
   331 				}
       
   332 				while (!finished);
       
   333 
       
   334 			commonChars.Set(aPossibilities[0].Mid(0, commonCharPos));
       
   335 			}
       
   336 		else
       
   337 			{
       
   338 			commonChars.Set(aPossibilities[0]);
       
   339 			}
       
   340 
       
   341 		TBool quote(EFalse);
       
   342 		if ((aPrefix && NeedsQuoting(*aPrefix)) || NeedsQuoting(commonChars) || (aSuffix && NeedsQuoting(*aSuffix)))
       
   343 			{
       
   344 			quote = ETrue;
       
   345 			}
       
   346 		if (quote)
       
   347 			{
       
   348 			completion->AppendL(KDoubleQuote);
       
   349 			}
       
   350 		if (aPrefix)
       
   351 			{
       
   352 			completion->AppendL(*aPrefix);
       
   353 			}
       
   354 		completion->AppendL(commonChars);
       
   355 		if ((numPossibilities == 1) && quote)
       
   356 			{
       
   357 			completion->AppendL(KDoubleQuote);
       
   358 			}
       
   359 		if (numPossibilities == 1)
       
   360 			{
       
   361 			if (aSuffix)
       
   362 				{
       
   363 				completion->AppendL(*aSuffix);
       
   364 				}
       
   365 			else
       
   366 				{
       
   367 				completion->AppendL(KSpace);
       
   368 				}
       
   369 			}
       
   370 		if (completion->Descriptor().Length() > 0)
       
   371 			{
       
   372 			aLine.Replace(aToken.Position(), completion->Descriptor());
       
   373 			}
       
   374 		CleanupStack::PopAndDestroy(completion);
       
   375 		}
       
   376 	}
       
   377 
       
   378 void CLineCompleter::CompleteEnvVarL(TConsoleLine& aLine, const TToken& aToken, const TChar& /*aEscapeChar*/) const
       
   379 	{
       
   380 	RArray<TPtrC> matchingVars;
       
   381 	CleanupClosePushL(matchingVars);
       
   382 	TPtrC envVar = aToken.String().Mid(1); // Lose the $
       
   383 
       
   384 	RPointerArray<HBufC> keys;
       
   385 	LtkUtils::CleanupResetAndDestroyPushL(keys);
       
   386 	iEnv.GetKeysL(keys);
       
   387 	
       
   388 	const TInt count = keys.Count();
       
   389 	for (TInt i = 0; i < count; i++)
       
   390 		{
       
   391 		const TDesC& key = *keys[i];
       
   392 		if (key.Left(envVar.Length()) == envVar)
       
   393 			{
       
   394 			matchingVars.AppendL(TPtrC(key));
       
   395 			}
       
   396 		}
       
   397 	CompleteL(aLine, aToken, matchingVars, &KDollar, NULL, ETrue);
       
   398 	CleanupStack::PopAndDestroy(2, &matchingVars); // keys, matchingVars
       
   399 	}
       
   400 
       
   401 void CLineCompleter::CompleteOptionL(TConsoleLine& aLine, const TToken& aToken, const TChar& /*aEscapeChar*/) const
       
   402 	{
       
   403 	RArray<TPtrC> matchingOptions;
       
   404 	CleanupClosePushL(matchingOptions);
       
   405 	TPtrC optFrag = aToken.String();
       
   406 	if (optFrag.Length() >= 2) optFrag.Set(optFrag.Mid(2)); // Lose the --
       
   407 	else if (optFrag.Length() == 1) optFrag.Set(TPtrC()); // single dash gets dropped completely
       
   408 
       
   409 	const IoUtils::RCommandOptionList& options = iLastUsedCif->Options();
       
   410 	const TInt count = options.Count();
       
   411 	for (TInt i = 0; i < count; i++)
       
   412 		{
       
   413 		const TDesC& optName = options[i].Name();;
       
   414 		if (optName.Left(optFrag.Length()) == optFrag)
       
   415 			{
       
   416 			matchingOptions.AppendL(TPtrC(optName));
       
   417 			}
       
   418 		}
       
   419 	CompleteL(aLine, aToken, matchingOptions, &KDashDash, NULL, ETrue);
       
   420 	CleanupStack::PopAndDestroy(&matchingOptions);
       
   421 	}
       
   422 	
       
   423