2012-09-05 13:37:39 +04:00
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author: Axel Naumann <axel@cern.ch>
2014-01-07 14:08:37 +04:00
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
2012-09-05 13:37:39 +04:00
//------------------------------------------------------------------------------
2012-12-06 13:54:20 +04:00
# include "cling/UserInterface/UserInterface.h"
2012-09-05 13:37:39 +04:00
2015-11-27 15:54:20 +03:00
# include "cling/Interpreter/Exception.h"
2012-12-06 13:54:20 +04:00
# include "cling/MetaProcessor/MetaProcessor.h"
2016-09-10 22:04:39 +03:00
# include "cling/Utils/Output.h"
2016-07-19 12:46:57 +03:00
# include "textinput/Callbacks.h"
2022-08-30 15:52:14 +03:00
# include "textinput/History.h"
2012-09-05 13:37:39 +04:00
# include "textinput/StreamReader.h"
# include "textinput/TerminalDisplay.h"
2022-08-30 15:52:14 +03:00
# include "textinput/TextInput.h"
2012-09-05 13:37:39 +04:00
2013-09-19 20:10:25 +04:00
# include "llvm/ADT/SmallString.h"
2013-09-06 23:36:03 +04:00
# include "llvm/Support/ErrorHandling.h"
2013-09-19 18:54:21 +04:00
# include "llvm/Support/Path.h"
2012-09-05 13:37:39 +04:00
2015-02-22 01:09:18 +03:00
# include "clang/Basic/LangOptions.h"
# include "clang/Frontend/CompilerInstance.h"
2016-07-07 11:47:58 +03:00
namespace {
///\brief Class that specialises the textinput TabCompletion to allow Cling
/// to code complete through its own textinput mechanism which is part of the
/// UserInterface.
///
2016-07-19 12:46:57 +03:00
class UITabCompletion : public textinput : : TabCompletion {
const cling : : Interpreter & m_ParentInterpreter ;
2022-08-30 10:52:07 +03:00
2016-07-07 11:47:58 +03:00
public :
2016-07-19 12:46:57 +03:00
UITabCompletion ( const cling : : Interpreter & Parent ) :
m_ParentInterpreter ( Parent ) { }
~ UITabCompletion ( ) { }
2016-07-07 11:47:58 +03:00
bool Complete ( textinput : : Text & Line /*in+out*/ ,
2016-07-19 12:46:57 +03:00
size_t & Cursor /*in+out*/ ,
textinput : : EditorRange & R /*out*/ ,
std : : vector < std : : string > & Completions /*out*/ ) override {
m_ParentInterpreter . codeComplete ( Line . GetText ( ) , Cursor , Completions ) ;
return true ;
2016-07-07 11:47:58 +03:00
}
} ;
2017-06-28 18:19:59 +03:00
///\brief Delays ~TextInput until after ~StreamReader and ~TerminalDisplay
///
class TextInputHolder {
textinput : : StreamReader * m_Reader ;
textinput : : TerminalDisplay * m_Display ;
textinput : : TextInput m_Input ;
public :
TextInputHolder ( llvm : : SmallString < 512 > & Hist )
: m_Reader ( textinput : : StreamReader : : Create ( ) ) ,
m_Display ( textinput : : TerminalDisplay : : Create ( ) ) ,
m_Input ( * m_Reader , * m_Display , Hist . empty ( ) ? 0 : Hist . c_str ( ) ) { }
~ TextInputHolder ( ) {
delete m_Reader ;
delete m_Display ;
}
textinput : : TextInput * operator - > ( ) { return & m_Input ; }
} ;
2022-08-30 16:12:57 +03:00
llvm : : SmallString < 512 > GetHistoryFilePath ( ) {
if ( getenv ( " CLING_NOHISTORY " ) ) {
return { } ;
}
if ( const char * HistFileEnvvar = std : : getenv ( " CLING_HISTFILE " ) ) {
return llvm : : StringRef { HistFileEnvvar } ;
}
// History file search order according to XDG Base Directory Specification:
//
// ${XDG_STATE_HOME}/cling/history
// ~/.local/state/cling/history
// ~/.cling_history
const char * StateHome = std : : getenv ( " XDG_STATE_HOME " ) ;
if ( ! StateHome ) {
StateHome = " ~/.local/state " ;
}
llvm : : SmallString < 512 > FilePath ;
if ( ! llvm : : sys : : fs : : real_path ( StateHome , FilePath , true ) ) {
// If xdg state home directory exists then create cling subdirectory if
// the latter does not exist.
if ( llvm : : sys : : fs : : is_directory ( FilePath ) & &
! llvm : : sys : : fs : : create_directory ( FilePath + = " /cling " ) ) {
return FilePath + = " /history " ;
}
}
if ( llvm : : sys : : path : : home_directory ( FilePath ) ) {
return FilePath + = " /.cling_history " ;
}
cling : : errs ( ) < < " Failed to create command history file \n " ;
return { } ;
}
2016-07-07 11:47:58 +03:00
}
2012-12-06 13:54:20 +04:00
namespace cling {
2013-09-09 16:32:37 +04:00
2012-12-06 13:54:20 +04:00
UserInterface : : UserInterface ( Interpreter & interp ) {
2016-09-10 22:04:39 +03:00
m_MetaProcessor . reset ( new MetaProcessor ( interp , cling : : outs ( ) ) ) ;
2016-09-05 21:06:32 +03:00
llvm : : install_fatal_error_handler ( & CompilationException : : throwingHandler ) ;
2012-09-05 13:37:39 +04:00
}
2012-12-06 13:54:20 +04:00
UserInterface : : ~ UserInterface ( ) { }
2012-09-05 13:37:39 +04:00
2012-12-06 13:54:20 +04:00
void UserInterface : : runInteractively ( bool nologo /* = false */ ) {
if ( ! nologo ) {
PrintLogo ( ) ;
2012-09-05 13:37:39 +04:00
}
2022-08-30 16:12:57 +03:00
auto histfilePath { GetHistoryFilePath ( ) } ;
2012-12-06 13:54:20 +04:00
2022-08-30 10:52:07 +03:00
const auto Completion =
std : : make_unique < UITabCompletion > ( m_MetaProcessor - > getInterpreter ( ) ) ;
2017-06-28 18:19:59 +03:00
TextInputHolder TI ( histfilePath ) ;
2012-12-06 13:54:20 +04:00
2022-08-30 10:52:07 +03:00
TI - > SetCompletion ( Completion . get ( ) ) ;
2016-06-23 19:09:06 +03:00
2022-08-30 15:52:14 +03:00
if ( const char * HistSizeEnvvar = std : : getenv ( " CLING_HISTSIZE " ) ) {
const size_t HistSize = std : : strtoull ( HistSizeEnvvar , nullptr , 0 ) ;
// std::strtoull() returns 0 if the parsing fails.
// zero HistSize will disable history logging to file.
// refer to textinput::History::AppendToFile()
TI - > SetHistoryMaxDepth ( HistSize ) ;
TI - > SetHistoryPruneLength (
static_cast < size_t > ( textinput : : History : : kPruneLengthDefault ) ) ;
}
2017-06-29 06:16:18 +03:00
bool Done = false ;
2016-08-31 06:09:14 +03:00
std : : string Line ;
std : : string Prompt ( " [cling]$ " ) ;
2017-06-29 06:16:18 +03:00
while ( ! Done ) {
2013-08-28 01:40:49 +04:00
try {
2013-08-28 01:00:35 +04:00
m_MetaProcessor - > getOuts ( ) . flush ( ) ;
2016-08-31 00:06:03 +03:00
{
MetaProcessor : : MaybeRedirectOutputRAII RAII ( * m_MetaProcessor ) ;
2017-06-28 18:19:59 +03:00
TI - > SetPrompt ( Prompt . c_str ( ) ) ;
Done = TI - > ReadInput ( ) = = textinput : : TextInput : : kRREOF ;
TI - > TakeInput ( Line ) ;
2017-06-29 06:16:18 +03:00
if ( Done & & Line . empty ( ) )
break ;
2013-08-28 01:00:35 +04:00
}
cling : : Interpreter : : CompilationResult compRes ;
2017-06-06 18:23:41 +03:00
const int indent = m_MetaProcessor - > process ( Line , compRes ) ;
2016-08-31 05:51:41 +03:00
// Quit requested?
2013-08-28 01:00:35 +04:00
if ( indent < 0 )
break ;
2016-08-31 06:09:14 +03:00
Prompt . replace ( 7 , std : : string : : npos ,
m_MetaProcessor - > getInterpreter ( ) . isRawInputEnabled ( ) ? " ! " : " $ " ) ;
2012-12-06 13:54:20 +04:00
2016-08-31 06:09:14 +03:00
// Continuation requested?
if ( indent > 0 ) {
Prompt . append ( 1 , ' ? ' ) ;
Prompt . append ( indent * 3 , ' ' ) ;
}
2012-12-06 13:54:20 +04:00
}
2015-12-01 17:41:16 +03:00
catch ( InterpreterException & e ) {
2016-12-19 16:57:41 +03:00
if ( ! e . diagnose ( ) ) {
cling : : errs ( ) < < " >>> Caught an interpreter exception! \n "
< < " >>> " < < e . what ( ) < < ' \n ' ;
}
2013-09-11 11:36:37 +04:00
}
catch ( std : : exception & e ) {
2016-09-10 22:04:39 +03:00
cling : : errs ( ) < < " >>> Caught a std::exception! \n "
2016-12-19 16:57:41 +03:00
< < " >>> " < < e . what ( ) < < ' \n ' ;
2013-09-05 18:43:20 +04:00
}
2013-08-28 01:40:49 +04:00
catch ( . . . ) {
2016-09-10 22:04:39 +03:00
cling : : errs ( ) < < " Exception occurred. Recovering... \n " ;
2013-08-28 01:40:49 +04:00
}
2012-12-06 13:54:20 +04:00
}
2016-08-31 00:06:03 +03:00
m_MetaProcessor - > getOuts ( ) . flush ( ) ;
2012-09-05 13:37:39 +04:00
}
2012-12-06 13:54:20 +04:00
void UserInterface : : PrintLogo ( ) {
2013-01-17 19:27:14 +04:00
llvm : : raw_ostream & outs = m_MetaProcessor - > getOuts ( ) ;
2015-02-22 01:09:18 +03:00
const clang : : LangOptions & LangOpts
= m_MetaProcessor - > getInterpreter ( ) . getCI ( ) - > getLangOpts ( ) ;
if ( LangOpts . CPlusPlus ) {
outs < < " \n "
" ****************** CLING ****************** \n "
" * Type C++ code and press enter to run it * \n "
" * Type .q to exit * \n "
" ******************************************* \n " ;
} else {
outs < < " \n "
" ***************** CLING ***************** \n "
" * Type C code and press enter to run it * \n "
" * Type .q to exit * \n "
" ***************************************** \n " ;
}
2012-12-06 13:54:20 +04:00
}
} // end namespace cling