--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/line_completer.cpp Wed Jun 23 15:52:26 2010 +0100
@@ -0,0 +1,423 @@
+// line_completer.cpp
+//
+// Copyright (c) 2006 - 2010 Accenture. All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Accenture - Initial contribution
+//
+
+#include <f32file.h>
+#include <fshell/ltkutils.h>
+#include "lexer.h"
+#include "command_factory.h"
+#include "line_completer.h"
+
+_LIT(KTab, "\t");
+_LIT(KWild, "*");
+_LIT(KDoubleQuote, "\"");
+_LIT(KSpace, " ");
+_LIT(KFileNameSlash, "\\");
+_LIT(KPipe, "|");
+_LIT(KDoublePipe, "||");
+_LIT(KAmpersand, "&");
+_LIT(KDoubleAmpersand, "&&");
+_LIT(KAmpersandPipe, "&|");
+_LIT(KSemicolon, ";");
+_LIT(KDollar, "$");
+_LIT(KDashDash, "--");
+_LIT(KDash, "-");
+
+CLineCompleter* CLineCompleter::NewL(RFs& aFs, CCommandFactory& aCommandFactory, IoUtils::CEnvironment& aEnv)
+ {
+ CLineCompleter* self = new(ELeave) CLineCompleter(aFs, aCommandFactory, aEnv);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+CLineCompleter::~CLineCompleter()
+ {
+ delete iLexer;
+ delete iLastUsedCif;
+ }
+
+void CLineCompleter::LcCompleteLineL(TConsoleLine& aLine, const TChar& aEscapeChar)
+ {
+ iLexer->Set(aLine.ContentsToCursor(), aEscapeChar);
+ TToken token(iLexer->NextToken());
+ TToken firstToken = token;
+ TToken prevToken(TToken::EString, KNullDesC, aLine.ContentsToCursor().Length());
+ TInt lastTokenIdx = 0;
+ while (iLexer->More())
+ {
+ // More than one token - skip to last.
+ prevToken = token;
+ token = iLexer->NextToken();
+ lastTokenIdx++;
+ }
+
+ switch (prevToken.Type())
+ {
+ case TToken::EPipe:
+ case TToken::EAmpersand:
+ case TToken::ESemicolon:
+ case TToken::EDoublePipe:
+ case TToken::EDoubleAmpersand:
+ case TToken::EAmpersandPipe:
+ CompleteCommandNameL(aLine, token, aEscapeChar);
+ break;
+ default:
+ if (token.Position() + token.String().Length() < aLine.ContentsToCursor().Length())
+ {
+ // Looks like there's whitespace after this token, so consider it to be completing the empty token.
+ token = TToken(TToken::EString, KNullDesC, aLine.ContentsToCursor().Length());
+ }
+ else if (lastTokenIdx == 0)
+ {
+ CompleteCommandNameL(aLine, token, aEscapeChar);
+ break;
+ }
+
+ if (token.String().Left(1) == KDollar)
+ {
+ CompleteEnvVarL(aLine, token, aEscapeChar);
+ }
+ else if (token.String() == KDash || token.String().Left(2) == KDashDash)
+ {
+ // Check the CIF is loaded and it's the right one
+ if (iLastUsedCif && iLastUsedCif->Name().CompareF(firstToken.String()) != 0)
+ {
+ delete iLastUsedCif;
+ iLastUsedCif = NULL;
+ }
+ TInt err = KErrNone;
+ if (iLastUsedCif == NULL)
+ {
+ // We need to supress StaticLeaveIfErrs from CCommandInfoFile::NewL causing a PrintError if there's no CIF for this command,
+ // hence the use of TRAP_QUIETLY.
+ TRAP_QUIETLY(err, iLastUsedCif = IoUtils::CCommandInfoFile::NewL(iFs, iEnv, firstToken.String()));
+ }
+ if (err == KErrNone)
+ {
+ CompleteOptionL(aLine, token, aEscapeChar);
+ }
+ }
+ else
+ {
+ CompleteFileNameL(aLine, token, aEscapeChar);
+ }
+ break;
+ }
+ }
+
+CLineCompleter::CLineCompleter(RFs& aFs, CCommandFactory& aCommandFactory, IoUtils::CEnvironment& aEnv)
+ : iFs(aFs), iCommandFactory(aCommandFactory), iEnv(aEnv)
+ {
+ }
+
+void CLineCompleter::ConstructL()
+ {
+ iLexer = CLexer::NewL();
+ iLexer->DefineTokenTypeL(TToken::EPipe, KPipe);
+ iLexer->DefineTokenTypeL(TToken::EAmpersand, KAmpersand);
+ iLexer->DefineTokenTypeL(TToken::ESemicolon, KSemicolon);
+ iLexer->DefineTokenTypeL(TToken::EDoublePipe, KDoublePipe);
+ iLexer->DefineTokenTypeL(TToken::EDoubleAmpersand, KDoubleAmpersand);
+ iLexer->DefineTokenTypeL(TToken::EAmpersandPipe, KAmpersandPipe);
+ }
+
+HBufC* SimplifyStringLC(const TDesC& aString, const TChar& aEscapeChar)
+ {
+ HBufC* buf = HBufC::NewLC(aString.Length());
+ TPtr bufPtr(buf->Des());
+ TLex lex(aString);
+ while (!lex.Eos())
+ {
+ TChar c = lex.Get();
+ if (c == aEscapeChar)
+ {
+ if (!lex.Eos())
+ {
+ bufPtr.Append(lex.Get());
+ }
+ }
+ else if (c == '\'')
+ {
+ while (!lex.Eos())
+ {
+ c = lex.Get();
+ if (c == '\'')
+ {
+ break;
+ }
+ else
+ {
+ bufPtr.Append(c);
+ }
+ }
+ }
+ else if (c == '"')
+ {
+ while (!lex.Eos())
+ {
+ c = lex.Get();
+ if (c == aEscapeChar)
+ {
+ bufPtr.Append(lex.Get());
+ }
+ else if (c == '"')
+ {
+ break;
+ }
+ else
+ {
+ bufPtr.Append(c);
+ }
+ }
+ }
+ else
+ {
+ bufPtr.Append(c);
+ }
+ }
+ return buf;
+ }
+
+void CLineCompleter::CompleteCommandNameL(TConsoleLine& aLine, const TToken& aToken, const TChar& aEscapeChar) const
+ {
+ RArray<TPtrC> commands;
+ CleanupClosePushL(commands);
+ iCommandFactory.ListCommandsL(commands);
+ HBufC* commandBuf = SimplifyStringLC(aToken.String(), aEscapeChar);
+
+ // Remove commands that don't match aLine.
+ TInt i;
+ for (i = (commands.Count() - 1); i >= 0; --i)
+ {
+ if (commands[i].Find(*commandBuf) != 0)
+ {
+ commands.Remove(i);
+ }
+ }
+
+ if (commands.Count() > 0)
+ {
+ CompleteL(aLine, aToken, commands, NULL, NULL);
+ }
+ else
+ {
+ CompleteFileNameL(aLine, aToken, aEscapeChar);
+ }
+ CleanupStack::PopAndDestroy(2, &commands);
+ }
+
+void RemovePartialElement(HBufC*& aBuf)
+ {
+ TPtr ptr(aBuf->Des());
+ while (ptr.Length() > 0)
+ {
+ if (ptr[ptr.Length() - 1] == KPathDelimiter)
+ {
+ break;
+ }
+ else
+ {
+ ptr.Delete(ptr.Length() - 1, 1);
+ }
+ }
+ }
+
+void CLineCompleter::CompleteFileNameL(TConsoleLine& aLine, const TToken& aToken, const TChar& aEscapeChar) const
+ {
+ HBufC* fileNameBuf = SimplifyStringLC(aToken.String(), aEscapeChar);
+ IoUtils::TFileName2 absFileName(*fileNameBuf);
+ RemovePartialElement(fileNameBuf);
+ absFileName.Append(KWild);
+ absFileName.MakeAbsoluteL(iEnv.Pwd());
+ CDir* files;
+ User::LeaveIfError(iFs.GetDir(absFileName, KEntryAttNormal | KEntryAttDir, ESortByName, files));
+ CleanupStack::PushL(files);
+ RArray<TPtrC> fileNames;
+ CleanupClosePushL(fileNames);
+ const TInt numFiles = files->Count();
+ if (numFiles == 1)
+ {
+ absFileName = absFileName.DriveAndPath();
+ absFileName.AppendComponentL((*files)[0].iName);
+ TEntry entry;
+ User::LeaveIfError(iFs.Entry(absFileName, entry));
+ User::LeaveIfError(fileNames.Append(absFileName.NameAndExt()));
+ CompleteL(aLine, aToken, fileNames, fileNameBuf, entry.IsDir() ? &KFileNameSlash : NULL);
+ }
+ else if (numFiles > 1)
+ {
+ for (TInt i = 0; i < numFiles; ++i)
+ {
+ User::LeaveIfError(fileNames.Append(TPtrC((*files)[i].iName)));
+ }
+ CompleteL(aLine, aToken, fileNames, fileNameBuf, NULL);
+ }
+ CleanupStack::PopAndDestroy(3, fileNameBuf);
+ }
+
+TBool NeedsQuoting(const TDesC& aDes)
+ {
+ return ((aDes.Locate(TChar(' ')) >= 0) || aDes.Locate(TChar('\'')) >= 0);
+ }
+
+void CLineCompleter::CompleteL(TConsoleLine& aLine, const TToken& aToken, const RArray<TPtrC> aPossibilities, const TDesC* aPrefix, const TDesC* aSuffix, TBool aPrefixIsPartOfToken) const
+ {
+ const TInt numPossibilities = aPossibilities.Count();
+
+ if (numPossibilities > 1)
+ {
+ // Fill out possibilities buffer.
+ IoUtils::CTextBuffer* possibilities = IoUtils::CTextBuffer::NewLC(0x100);
+ for (TInt i = 0; i < numPossibilities; ++i)
+ {
+ if (aPrefixIsPartOfToken) possibilities->AppendL(*aPrefix);
+ possibilities->AppendL(aPossibilities[i]);
+ if (i != (numPossibilities - 1))
+ {
+ possibilities->AppendL(KTab);
+ }
+ }
+
+ aLine.PrintCompletionPossibilitiesL(possibilities->Descriptor());
+ CleanupStack::PopAndDestroy(possibilities);
+ }
+
+ if (numPossibilities > 0)
+ {
+ IoUtils::CTextBuffer* completion = IoUtils::CTextBuffer::NewLC(0x100);
+ TPtrC commonChars(NULL, 0);
+ if (numPossibilities > 1)
+ {
+ // Find common leading characters of the possibilities.
+ TInt commonCharPos = -1;
+ TBool finished(EFalse);
+ do
+ {
+ ++commonCharPos;
+ TChar character(0);
+ for (TInt i = 0; i < numPossibilities; ++i)
+ {
+ if (commonCharPos >= aPossibilities[i].Length())
+ {
+ finished = ETrue;
+ break;
+ }
+ else if (i == 0)
+ {
+ character = aPossibilities[0][commonCharPos];
+ character.Fold();
+ }
+ else
+ {
+ TChar c(aPossibilities[i][commonCharPos]);
+ c.Fold();
+ if (c != character)
+ {
+ finished = ETrue;
+ break;
+ }
+ }
+ }
+ }
+ while (!finished);
+
+ commonChars.Set(aPossibilities[0].Mid(0, commonCharPos));
+ }
+ else
+ {
+ commonChars.Set(aPossibilities[0]);
+ }
+
+ TBool quote(EFalse);
+ if ((aPrefix && NeedsQuoting(*aPrefix)) || NeedsQuoting(commonChars) || (aSuffix && NeedsQuoting(*aSuffix)))
+ {
+ quote = ETrue;
+ }
+ if (quote)
+ {
+ completion->AppendL(KDoubleQuote);
+ }
+ if (aPrefix)
+ {
+ completion->AppendL(*aPrefix);
+ }
+ completion->AppendL(commonChars);
+ if ((numPossibilities == 1) && quote)
+ {
+ completion->AppendL(KDoubleQuote);
+ }
+ if (numPossibilities == 1)
+ {
+ if (aSuffix)
+ {
+ completion->AppendL(*aSuffix);
+ }
+ else
+ {
+ completion->AppendL(KSpace);
+ }
+ }
+ if (completion->Descriptor().Length() > 0)
+ {
+ aLine.Replace(aToken.Position(), completion->Descriptor());
+ }
+ CleanupStack::PopAndDestroy(completion);
+ }
+ }
+
+void CLineCompleter::CompleteEnvVarL(TConsoleLine& aLine, const TToken& aToken, const TChar& /*aEscapeChar*/) const
+ {
+ RArray<TPtrC> matchingVars;
+ CleanupClosePushL(matchingVars);
+ TPtrC envVar = aToken.String().Mid(1); // Lose the $
+
+ RPointerArray<HBufC> keys;
+ LtkUtils::CleanupResetAndDestroyPushL(keys);
+ iEnv.GetKeysL(keys);
+
+ const TInt count = keys.Count();
+ for (TInt i = 0; i < count; i++)
+ {
+ const TDesC& key = *keys[i];
+ if (key.Left(envVar.Length()) == envVar)
+ {
+ matchingVars.AppendL(TPtrC(key));
+ }
+ }
+ CompleteL(aLine, aToken, matchingVars, &KDollar, NULL, ETrue);
+ CleanupStack::PopAndDestroy(2, &matchingVars); // keys, matchingVars
+ }
+
+void CLineCompleter::CompleteOptionL(TConsoleLine& aLine, const TToken& aToken, const TChar& /*aEscapeChar*/) const
+ {
+ RArray<TPtrC> matchingOptions;
+ CleanupClosePushL(matchingOptions);
+ TPtrC optFrag = aToken.String();
+ if (optFrag.Length() >= 2) optFrag.Set(optFrag.Mid(2)); // Lose the --
+ else if (optFrag.Length() == 1) optFrag.Set(TPtrC()); // single dash gets dropped completely
+
+ const IoUtils::RCommandOptionList& options = iLastUsedCif->Options();
+ const TInt count = options.Count();
+ for (TInt i = 0; i < count; i++)
+ {
+ const TDesC& optName = options[i].Name();;
+ if (optName.Left(optFrag.Length()) == optFrag)
+ {
+ matchingOptions.AppendL(TPtrC(optName));
+ }
+ }
+ CompleteL(aLine, aToken, matchingOptions, &KDashDash, NULL, ETrue);
+ CleanupStack::PopAndDestroy(&matchingOptions);
+ }
+
+