JVariableList::MatchResult
JVariableList::FindUniqueVarName
	(
	const JCharacter*	prefix,
	JIndex*				index,
	JString*			maxPrefix
	)
	const
{
	assert( !JStringEmpty(prefix) );

	const JSize count = GetElementCount();
	JArray<JIndex> matchList;

	for (JIndex i=1; i<=count; i++)
		{
		const JString& name = GetVariableName(i);
		if (name == prefix)
			{
			*index     = i;
			*maxPrefix = name;
			return kSingleMatch;
			}
		else if (JStringBeginsWith(name, name.GetLength(), prefix))
			{
			matchList.AppendElement(i);
			}
		}

	const JSize matchCount = matchList.GetElementCount();
	if (matchCount == 0)
		{
		*index = 0;
		maxPrefix->Clear();
		return kNoMatch;
		}
	else if (matchCount == 1)
		{
		*index     = matchList.GetElement(1);
		*maxPrefix = GetVariableName(*index);
		return kSingleMatch;
		}
	else
		{
		*maxPrefix = GetVariableName( matchList.GetElement(1) );
		for (JIndex i=2; i<=matchCount; i++)
			{
			const JString& varName   = GetVariableName( matchList.GetElement(i) );
			const JSize matchLength  = JCalcMatchLength(*maxPrefix, varName);
			const JSize prefixLength = maxPrefix->GetLength();
			if (matchLength < prefixLength)
				{
				maxPrefix->RemoveSubstring(matchLength+1, prefixLength);
				}
			}
		*index = 0;
		return kMultipleMatch;
		}
}
JString
JConvertToRelativePath
	(
	const JCharacter* origPath,
	const JCharacter* origBase
	)
{
	// Check that they are both absolute paths.

	assert( origPath != NULL && origPath[0] == '/' &&
			origBase != NULL && origBase[0] == '/' );

	// Remove extra directory separators
	// and make sure that base ends with one.

	JString path = origPath;
	JCleanPath(&path);

	JString base = origBase;
	JCleanPath(&base);
	JAppendDirSeparator(&base);

	// Find and remove the matching directories at the beginning.
	// The while loop backs us up so we only consider complete directory names.

	JBoolean hadTDS = kJTrue;
	if (path.GetLastCharacter() != '/')
		{
		path.AppendCharacter('/');
		hadTDS = kJFalse;
		}

	JSize matchLength = JCalcMatchLength(path, base);

	if (!hadTDS)
		{
		path.RemoveSubstring(path.GetLength(), path.GetLength());
		}

	while (base.GetCharacter(matchLength) != '/')
		{
		matchLength--;
		}
	assert( matchLength >= 1 );
	if (matchLength == 1)
		{
		return path;
		}

	if (matchLength > path.GetLength())
		{
		base.RemoveSubstring(matchLength, matchLength);
		matchLength--;
		}

	path.RemoveSubstring(1, matchLength);
	base.RemoveSubstring(1, matchLength);

	if (base.IsEmpty())
		{
		path.Prepend("./");
		return path;
		}

	// The number of remaining directory separators in base
	// is the number of levels to go up.

	JSize upCount = 0;

	const JSize baseLength = base.GetLength();
	for (JIndex i=1; i<=baseLength; i++)
		{
		if (base.GetCharacter(i) == '/')
			{
			upCount++;
			path.Prepend("../");
			}
		}
	assert( upCount > 0 );

	return path;
}
JBoolean
JXPathInput::Complete
	(
	JXInputField*				te,
	const JCharacter*			basePath,	// can be NULL
	JDirInfo*					completer,
	JXStringCompletionMenu**	menu		// constructed if NULL
	)
{
	// only complete if caret is at end of text

	JIndex caretIndex;
	if (!te->GetCaretLocation(&caretIndex) ||
		caretIndex != te->GetTextLength()+1)
		{
		return kJFalse;
		}

	// catch empty path

	if (te->IsEmpty())
		{
		const JString path = JGetRootDirectory();
		te->Paste(path);
		return kJTrue;
		}

	// convert to absolute path

	JString fullName;
	if (!JExpandHomeDirShortcut(te->GetText(), &fullName))
		{
		return kJFalse;
		}
	if (JIsRelativePath(fullName))
		{
		if (JStringEmpty(basePath))
			{
			return kJFalse;
			}
		fullName = JCombinePathAndName(basePath, fullName);
		}

	// if completing ~ rather than ~/

	if (fullName.EndsWith(ACE_DIRECTORY_SEPARATOR_STR) &&
		!(te->GetText()).EndsWith(ACE_DIRECTORY_SEPARATOR_STR))
		{
		JStripTrailingDirSeparator(&fullName);
		}

	// get path and wildcard filter

	JString path, name;
	if (fullName.EndsWith(ACE_DIRECTORY_SEPARATOR_STR))
		{
		path = fullName;
		name = "*";
		}
	else
		{
		JSplitPathAndName(fullName, &path, &name);
		name.AppendCharacter('*');
		}

	// build completion list

	if (!(completer->GoTo(path)).OK())
		{
		return kJFalse;
		}

	completer->SetWildcardFilter(name, kJFalse, kJTrue);
	if (completer->IsEmpty())
		{
		return kJFalse;
		}

	// check for characters common to all matches

	JString maxPrefix = jGetFullName(completer, 1);

	const JSize matchCount = completer->GetEntryCount();
	JString entryName;
	for (JIndex i=2; i<=matchCount; i++)
		{
		entryName                = jGetFullName(completer, i);
		const JSize matchLength  = JCalcMatchLength(maxPrefix, entryName);
		const JSize prefixLength = maxPrefix.GetLength();
		if (matchLength < prefixLength)
			{
			maxPrefix.RemoveSubstring(matchLength+1, prefixLength);
			}
		}

	// use the completion list

	if (matchCount > 0 &&
		maxPrefix.GetLength() > fullName.GetLength())
		{
		maxPrefix.RemoveSubstring(1, fullName.GetLength());
		if (matchCount == 1 && (completer->GetEntry(1)).IsDirectory())
			{
			JAppendDirSeparator(&maxPrefix);
			}
		te->Paste(maxPrefix);		// so Undo removes completion

		if (*menu != NULL)
			{
			(**menu).ClearRequestCount();
			}

		return kJTrue;
		}
	else if (matchCount > 1)
		{
		if (*menu == NULL)
			{
			*menu = new JXStringCompletionMenu(te, kJFalse);
			assert( *menu != NULL );
			}
		else
			{
			(**menu).RemoveAllItems();
			}

		for (JIndex i=1; i<=matchCount; i++)
			{
			entryName = jGetName(completer, i);
			(**menu).AddString(entryName);
			}

		(**menu).CompletionRequested(name.GetLength()-1);
		return kJTrue;
		}
	else
		{
		return kJFalse;
		}
}
Exemple #4
0
void
GDBGetCompletions::HandleSuccess
	(
	const JString& data
	)
{
	JPtrArray<JString> lines(JPtrArrayT::kDeleteAll);
	lines.SetSortOrder(JOrderedSetT::kSortAscending);
	lines.SetCompareFunction(JCompareStringsCaseSensitive);

	// loop through each line and add each one to our list

	JIndex i = 1, j = 1;
	while (data.LocateNextSubstring("\n", &j))
		{
		if (j > i)
			{
			JString* s = new JString(data, JIndexRange(i, j-1));
			assert( s != NULL );
			s->TrimWhitespace();
			if (s->IsEmpty() || !lines.InsertSorted(s, kJFalse))
				{
				delete s;
				}
			}
		i = j+1;
		j = i;
		}

	if (i <= data.GetLength())
		{
		JString* s = new JString(data, JIndexRange(i, data.GetLength()));
		assert( s != NULL );
		s->TrimWhitespace();
		if (s->IsEmpty() || !lines.InsertSorted(s, kJFalse))
			{
			delete s;
			}
		}

	if (lines.IsEmpty())
		{
		(itsInput->GetDisplay())->Beep();
		return;
		}

	// Check if we're done. If we find our test prefix in the array, and
	// the array has only one element, we're done.  Otherwise, our test
	// prefix is the 'start' of several possible commands.

	const JSize stringCount = lines.GetElementCount();
	JBoolean found;
	JIndex startIndex = lines.SearchSorted1(&itsPrefix, JOrderedSetT::kAnyMatch, &found);
	if (found)
		{
		if (stringCount == 1)
			{
			// The input text is already complete.  We just need to add a
			// space at the end to show that it is a complete word.

			itsPrefix += " ";
			}
		else
			{
			// We can't add anything to what the user has types, so we show
			// all possible completions.

			itsHistory->PlaceCursorAtEnd();
			itsHistory->Paste("\n");
			itsHistory->Paste(data);
			}
		itsInput->SetText(itsPrefix);
		itsInput->SetCaretLocation(itsInput->GetTextLength() + 1);
		return;
		}

	JString maxPrefix;

	maxPrefix = *(lines.NthElement(startIndex));
	if (stringCount == 1)
		{
		// There's only one completion, which must be what we meant so we
		// fill in the input with this word.

		maxPrefix += " ";
		itsInput->SetText(maxPrefix);
		itsInput->SetCaretLocation(itsInput->GetTextLength() + 1);
		return;
		}

	for (JIndex i=startIndex+1; i<=stringCount; i++)
		{
		const JString* s = lines.NthElement(i);
		const JSize matchLength  = JCalcMatchLength(maxPrefix, *s);
		const JSize prefixLength = maxPrefix.GetLength();
		if (matchLength < prefixLength)
			{
			maxPrefix.RemoveSubstring(matchLength+1, prefixLength);
			}
		}

	if (maxPrefix == itsPrefix)
		{
		// The input text is all that the words have in common so we can't
		// add anything to the input. We therefore need to dump everything
		// to the history window.

		itsHistory->PlaceCursorAtEnd();
		itsHistory->Paste("\n");
		itsHistory->Paste(data);
		}
	else
		{
		itsInput->SetText(maxPrefix);
		itsInput->SetCaretLocation(itsInput->GetTextLength() + 1);
		}
}
JSize
CBStringCompleter::Complete
	(
	const JString&			prefix,
	JString*				maxPrefix,
	JXStringCompletionMenu*	menu
	)
	const
{
	menu->RemoveAllItems();

	JBoolean found;
	const JIndex startIndex =
		itsStringList->SearchSorted1(const_cast<JString*>(&prefix),
									 JOrderedSetT::kAnyMatch, &found);

	const JSize stringCount = itsStringList->GetElementCount();
	if (startIndex > stringCount)
		{
		maxPrefix->Clear();
		return 0;
		}

	JPtrArray<JString> matchList(JPtrArrayT::kForgetAll, 100);
	*maxPrefix = *(itsStringList->NthElement(startIndex));

	JSize matchCount   = 0;
	JBoolean addString = kJTrue;
	for (JIndex i=startIndex; i<=stringCount; i++)
		{
		const JString* s = itsStringList->NthElement(i);
		if (!s->BeginsWith(prefix, itsCaseSensitiveFlag))
			{
			break;
			}

		if (matchCount > 0)
			{
			const JSize matchLength  = JCalcMatchLength(*maxPrefix, *s, itsCaseSensitiveFlag);
			const JSize prefixLength = maxPrefix->GetLength();
			if (matchLength < prefixLength)
				{
				maxPrefix->RemoveSubstring(matchLength+1, prefixLength);
				}
			}

		matchCount++;
		if (itsCaseSensitiveFlag)
			{
			matchList.Append(const_cast<JString*>(s));
			}
		else if (addString)
			{
			JString s1 = *s;
			MatchCase(prefix, &s1);
			addString = menu->AddString(s1);	// must process all to get maxPrefix
			}
		}

	if (itsCaseSensitiveFlag && matchCount > 0)
		{
		matchList.SetSortOrder(JOrderedSetT::kSortAscending);
		matchList.SetCompareFunction(JCompareStringsCaseInsensitive);
		matchList.Sort();

		assert( matchCount == matchList.GetElementCount() );
		for (JIndex i=1; i<=matchCount; i++)
			{
			if (!menu->AddString(*(matchList.NthElement(i))))
				{
				matchCount = i-1;
				break;
				}
			}
		}
	else if (!itsCaseSensitiveFlag)
		{
		MatchCase(prefix, maxPrefix);
		}

	return matchCount;
}