unix-conf/.emacs.d/elpa/ac-clang-20150906.1008/clang-server/ClangServer.cpp
2016-02-18 14:53:30 +01:00

440 lines
15 KiB
C++

/* -*- mode: c++ ; coding: utf-8-unix -*- */
/* last updated : 2015/07/25.03:24:29 */
/*
* Copyright (c) 2013-2015 yaruopooner [https://github.com/yaruopooner]
*
* This file is part of ac-clang.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*================================================================================================*/
/* Comment */
/*================================================================================================*/
/*================================================================================================*/
/* Include Files */
/*================================================================================================*/
#include "ClangServer.hpp"
using namespace std;
/*================================================================================================*/
/* Internal Function Definitions Section */
/*================================================================================================*/
namespace
{
std::string GetClangVersion( void )
{
CXString version_text = clang_getClangVersion();
const std::string clang_version = clang_getCString( version_text );
clang_disposeString( version_text );
return clang_version;
}
}
/*================================================================================================*/
/* Global Class Method Definitions Section */
/*================================================================================================*/
/*
---- SERVER MESSAGE ----
- GET_SPECIFIC_PROPERTIES: return clang-server specific properties
Message format:
command_type:Server
command_name:GET_SPECIFIC_PROPERTIES
- GET_CLANG_VERSION: return libclang version (libclang.lib/a)
Message format:
command_type:Server
command_name:GET_CLANG_VERSION
- SET_CLANG_PARAMETERS: setup libclang behavior parameters
Message format:
command_type:Server
command_name:SET_CLANG_PARAMETERS
translation_unit_flags:[#flag_name0|flag_name1|...#]
complete_at_flags:[#flag_name0|flag_name1|...#]
- CREATE_SESSION: create session.
Message format:
command_type:Server
command_name:CREATE_SESSION
session_name::[#session_name#]
- DELETE_SESSION: delete session.
Message format:
command_type:Server
command_name:DELETE_SESSION
session_name::[#session_name#]
- RESET: reset the clang server(all session delete. context reallocate)
Message format:
command_type:Server
command_name:RESET
- SHUTDOWN: shutdown the clang server (this program)
Message format:
command_type:Server
command_name:SHUTDOWN
---- SESSION MESSAGE ----
- SUSPEND: delete CXTranslationUnit
Message format:
command_type:Session
command_name:SUSPEND
session_name:[#session_name#]
- RESUME: realloc CXTranslationUnit
Message format:
command_type:Session
command_name:RESUME
session_name:[#session_name#]
- SET_CFLAGS: Specify CFLAGS argument passing to clang parser.
Message format:
command_type:Session
command_name:SET_CFLAGS
session_name:[#session_name#]
num_cflags:[#num_cflags#]
arg1 arg2 ...... (there should be num_cflags items here)
CFLAGS's white space must be accept.
a separator is '\n'.
source_length:[#source_length#]
<# SOURCE CODE #>
- SET_SOURCECODE: Update the source code in the source buffer
Message format:
command_type:Session
command_name:SET_SOURCECODE
session_name:[#session_name#]
source_length:[#source_length#]
<# SOURCE CODE #>
- REPARSE: Reparse the source code
command_type:Session
command_name:REPARSE
session_name:[#session_name#]
- COMPLETION: Do code completion at a specified point.
Message format:
command_type:Session
command_name:COMPLETION
session_name:[#session_name#]
line:[#line#]
column:[#column#]
source_length:[#source_length#]
<# SOURCE CODE #>
- SYNTAXCHECK: Retrieve diagnostic messages
Message format:
command_type:Session
command_name:SYNTAXCHECK
session_name:[#session_name#]
source_length:[#source_length#]
<# SOURCE CODE #>
- INCLUSION: Get location of inclusion file at point
Message format:
command_type:Session
command_name:INCLUSION
session_name:[#session_name#]
line:[#line#]
column:[#column#]
source_length:[#source_length#]
<# SOURCE CODE #>
- DEFINITION: Get location of definition at point
Message format:
command_type:Session
command_name:DEFINITION
session_name:[#session_name#]
line:[#line#]
column:[#column#]
source_length:[#source_length#]
<# SOURCE CODE #>
- DECLARATION: Get location of declaration at point
Message format:
command_type:Session
command_name:DECLARATION
session_name:[#session_name#]
line:[#line#]
column:[#column#]
source_length:[#source_length#]
<# SOURCE CODE #>
- SMARTJUMP: Get location of inclusion, definition, declaration at point. If that fails, it will search following.
inclusion -> finish, definition -> declaration -> finish.
Message format:
command_type:Session
command_name:SMARTJUMP
session_name:[#session_name#]
line:[#line#]
column:[#column#]
source_length:[#source_length#]
<# SOURCE CODE #>
*/
ClangServer::ClangServer( const Specification& specification )
:
m_Status( kStatus_Running )
, m_Specification( specification )
{
// setup stream buffer size
m_Specification.m_StdinBufferSize = std::max( m_Specification.m_StdinBufferSize, static_cast< size_t >( Specification::kStreamBuffer_UnitSize ) );
m_Specification.m_StdoutBufferSize = std::max( m_Specification.m_StdoutBufferSize, static_cast< size_t >( Specification::kStreamBuffer_UnitSize ) );
::setvbuf( stdin, nullptr, _IOFBF, m_Specification.m_StdinBufferSize );
::setvbuf( stdout, nullptr, _IOFBF, m_Specification.m_StdoutBufferSize );
// server command
m_ServerCommands.insert( ServerHandleMap::value_type( "GET_SPECIFICATION", std::mem_fn( &ClangServer::commandGetSpecification ) ) );
m_ServerCommands.insert( ServerHandleMap::value_type( "GET_CLANG_VERSION", std::mem_fn( &ClangServer::commandGetClangVersion ) ) );
m_ServerCommands.insert( ServerHandleMap::value_type( "SET_CLANG_PARAMETERS", std::mem_fn( &ClangServer::commandSetClangParameters ) ) );
m_ServerCommands.insert( ServerHandleMap::value_type( "CREATE_SESSION", std::mem_fn( &ClangServer::commandCreateSession ) ) );
m_ServerCommands.insert( ServerHandleMap::value_type( "DELETE_SESSION", std::mem_fn( &ClangServer::commandDeleteSession ) ) );
m_ServerCommands.insert( ServerHandleMap::value_type( "RESET", std::mem_fn( &ClangServer::commandReset ) ) );
m_ServerCommands.insert( ServerHandleMap::value_type( "SHUTDOWN", std::mem_fn( &ClangServer::commandShutdown ) ) );
// session command
m_SessionCommands.insert( SessionHandleMap::value_type( "SUSPEND", std::mem_fn( &ClangSession::commandSuspend ) ) );
m_SessionCommands.insert( SessionHandleMap::value_type( "RESUME", std::mem_fn( &ClangSession::commandResume ) ) );
m_SessionCommands.insert( SessionHandleMap::value_type( "SET_CFLAGS", std::mem_fn( &ClangSession::commandSetCFlags ) ) );
m_SessionCommands.insert( SessionHandleMap::value_type( "SET_SOURCECODE", std::mem_fn( &ClangSession::commandSetSourceCode ) ) );
m_SessionCommands.insert( SessionHandleMap::value_type( "REPARSE", std::mem_fn( &ClangSession::commandReparse ) ) );
m_SessionCommands.insert( SessionHandleMap::value_type( "COMPLETION", std::mem_fn( &ClangSession::commandCompletion ) ) );
m_SessionCommands.insert( SessionHandleMap::value_type( "SYNTAXCHECK", std::mem_fn( &ClangSession::commandDiagnostics ) ) );
m_SessionCommands.insert( SessionHandleMap::value_type( "INCLUSION", std::mem_fn( &ClangSession::commandInclusion ) ) );
m_SessionCommands.insert( SessionHandleMap::value_type( "DEFINITION", std::mem_fn( &ClangSession::commandDefinition ) ) );
m_SessionCommands.insert( SessionHandleMap::value_type( "DECLARATION", std::mem_fn( &ClangSession::commandDeclaration ) ) );
m_SessionCommands.insert( SessionHandleMap::value_type( "SMARTJUMP", std::mem_fn( &ClangSession::commandSmartJump ) ) );
// display initial specification
commandGetSpecification();
}
ClangServer::~ClangServer( void )
{
// m_Sessions must be destruction early than m_Context.
// Because m_Sessions depend to m_Context.
m_Sessions.clear();
}
void ClangServer::commandGetSpecification( void )
{
const std::string server_version = CLANG_SERVER_VERSION;
const std::string clang_version = ::GetClangVersion();
const std::string generate = CMAKE_GENERATOR "/" CMAKE_HOST_SYSTEM_PROCESSOR;
m_Writer.Write( "-------- Clang-Server Specification --------\n" );
m_Writer.Write( "Server Version : %s\n", server_version.c_str() );
m_Writer.Write( "Clang Version : %s\n", clang_version.c_str() );
m_Writer.Write( "Generate : %s\n", generate.c_str() );
// m_Writer.Write( "Log File : %s\n", m_Specification.m_LogFile.c_str() );
m_Writer.Write( "STDIN Buffer Size : %d bytes\n", m_Specification.m_StdinBufferSize );
m_Writer.Write( "STDOUT Buffer Size : %d bytes\n", m_Specification.m_StdoutBufferSize );
m_Writer.Flush();
}
void ClangServer::commandGetClangVersion( void )
{
const std::string clang_version = ::GetClangVersion();
m_Writer.Write( "%s ", clang_version.c_str() );
m_Writer.Flush();
}
void ClangServer::commandSetClangParameters( void )
{
const string translation_unit_flags = m_Reader.ReadToken( "translation_unit_flags:%s" );
const string complete_at_flags = m_Reader.ReadToken( "complete_at_flags:%s" );
const uint32_t translation_unit_flags_value = ClangFlagConverters::GetCXTranslationUnitFlags().GetValue( translation_unit_flags );
const uint32_t complete_at_flags_value = ClangFlagConverters::GetCXCodeCompleteFlags().GetValue( complete_at_flags );
uint32_t complete_results_limit;
m_Reader.ReadToken( "complete_results_limit:%d", complete_results_limit );
m_Context.SetTranslationUnitFlags( translation_unit_flags_value );
m_Context.SetCompleteAtFlags( complete_at_flags_value );
m_Context.SetCompleteResultsLimit( complete_results_limit );
}
void ClangServer::commandCreateSession( void )
{
const string session_name = m_Reader.ReadToken( "session_name:%s" );
// search session
Dictionary::iterator session_it = m_Sessions.find( session_name );
if ( session_it == m_Sessions.end() )
{
// not found session
// allocate & setup new session
std::shared_ptr< ClangSession > new_session( std::make_shared< ClangSession >( session_name, m_Context, m_Reader, m_Writer ) );
std::pair< Dictionary::iterator, bool > result = m_Sessions.insert( Dictionary::value_type( session_name, new_session ) );
if ( result.second )
{
// success
new_session->Allocate();
}
}
else
{
// already exist
}
}
void ClangServer::commandDeleteSession( void )
{
const string session_name = m_Reader.ReadToken( "session_name:%s" );
// search session
Dictionary::iterator session_it = m_Sessions.find( session_name );
if ( session_it != m_Sessions.end() )
{
// session_it->second->Deallocate();
m_Sessions.erase( session_it );
}
}
void ClangServer::commandReset( void )
{
m_Sessions.clear();
m_Context.Deallocate();
m_Context.Allocate();
}
void ClangServer::commandShutdown( void )
{
m_Status = kStatus_Exit;
}
void ClangServer::ParseServerCommand( void )
{
const string command_name = m_Reader.ReadToken( "command_name:%s" );
ServerHandleMap::iterator command_it = m_ServerCommands.find( command_name );
// execute command handler
if ( command_it != m_ServerCommands.end() )
{
command_it->second( *this );
}
else
{
// unknown command
}
}
void ClangServer::ParseSessionCommand( void )
{
const string command_name = m_Reader.ReadToken( "command_name:%s" );
const string session_name = m_Reader.ReadToken( "session_name:%s" );
if ( session_name.empty() )
{
return;
}
// search session
Dictionary::iterator session_it = m_Sessions.find( session_name );
// execute command handler
if ( session_it != m_Sessions.end() )
{
SessionHandleMap::iterator command_it = m_SessionCommands.find( command_name );
if ( command_it != m_SessionCommands.end() )
{
command_it->second( *session_it->second );
}
else
{
// session not found
}
}
else
{
// unknown command
}
}
void ClangServer::ParseCommand( void )
{
do
{
const string command_type = m_Reader.ReadToken( "command_type:%s" );
if ( command_type == "Server" )
{
ParseServerCommand();
}
else if ( command_type == "Session" )
{
ParseSessionCommand();
}
else
{
// unknown command type
}
} while ( m_Status != kStatus_Exit );
}
/*================================================================================================*/
/* EOF */
/*================================================================================================*/