diff -r 000000000000 -r 7f656887cf89 core/src/line_completer.cpp --- /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 +#include +#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 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 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 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 matchingVars; + CleanupClosePushL(matchingVars); + TPtrC envVar = aToken.String().Mid(1); // Lose the $ + + RPointerArray 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 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); + } + +