Skip to content

Commit

Permalink
Add support for finding references (#57)
Browse files Browse the repository at this point in the history
* initial work on references
can get references for variables in src file

* saving work

* sync cache

* saving work, can get include user func refs

* work on workspace cache

* saving work on cache

* Add RelWithDebInfo to possible native locations

* Move cache creation to end of onInitailize; fix tests

* Some cleanup

* Add support for module function references

* Fix references for constants

* Implement open tabs support for diagnostics

* Add definition support for program parameter

* Add support for program and function parameter references

* Some cleanup; run tests in-band

* Update polserver to cd55c81

* Remove unused mutex
  • Loading branch information
KevinEady committed Feb 23, 2024
1 parent 5d6c704 commit c3d2a48
Show file tree
Hide file tree
Showing 18 changed files with 1,113 additions and 61 deletions.
21 changes: 20 additions & 1 deletion client/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
* ------------------------------------------------------------------------------------------ */

import * as path from 'path';
import { workspace, ExtensionContext } from 'vscode';
import { workspace, window, ExtensionContext, Tab } from 'vscode';

import { activatePolDebug } from './activatePolDebug';
import {
LanguageClient,
LanguageClientOptions,
ServerOptions,
State,
TransportKind
} from 'vscode-languageclient/node';

Expand Down Expand Up @@ -51,6 +52,14 @@ export function activate(context: ExtensionContext) {
}
};

const sendOpenTabs = () => {
const uris = [... new Set<string>(window.tabGroups.all.reduce((p, c) => (p.push(...c.tabs.reduce((p, c) => (p.push((c.input as any).uri.toString() as string), p), new Array<string>())), p), new Array<string>()))];
return client.sendNotification('$workspace/openTabs', {
uris
});
};


// Create the language client and start the client.
client = new LanguageClient(
'escript',
Expand All @@ -59,6 +68,12 @@ export function activate(context: ExtensionContext) {
clientOptions
);

client.onDidChangeState(async e => {
if (e.newState === State.Running) {
return sendOpenTabs();
}
});

// Start the client. This will also launch the server
client.start();

Expand All @@ -69,6 +84,10 @@ export function activate(context: ExtensionContext) {
configuration: workspace.getConfiguration('escript')
});
});

window.tabGroups.onDidChangeTabs(e => {
return sendOpenTabs();
});
}

export function deactivate(): Thenable<void> | undefined {
Expand Down
2 changes: 1 addition & 1 deletion native/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ target_link_libraries(${PROJECT_NAME} PUBLIC
${CMAKE_JS_LIB}
)
target_compile_definitions(${PROJECT_NAME} PRIVATE
NAPI_VERSION=5
NAPI_VERSION=8
)

if(MSVC)
Expand Down
1 change: 1 addition & 0 deletions native/cpp/binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Napi::Object Init( Napi::Env env, Napi::Object exports )
Napi::Function::New( env, &VSCodeEscript::ExtensionConfiguration::Get );
exports.Set( Napi::String::New( env, "ExtensionConfiguration" ), ExtensionConfiguration );

env.SetInstanceData( new Napi::Reference<Napi::Object>( Napi::Persistent( exports ) ) );
return exports;
}

Expand Down
17 changes: 17 additions & 0 deletions native/cpp/compiler/DefinitionBuilder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,22 @@ std::optional<SourceLocation> DefinitionBuilder::get_program( const std::string&
return program->source_location;
}

std::optional<Pol::Bscript::Compiler::SourceLocation> DefinitionBuilder::get_program_parameter(
const std::string& name )
{
if ( auto& program = workspace.program )
{
for ( auto& child : program->parameter_list().children )
{
auto& program_parameter = static_cast<ProgramParameterDeclaration&>( *child );
if ( program_parameter.name == name )
{
return program_parameter.source_location;
}
}
}
return {};
}


} // namespace VSCodeEscript::CompilerExt
2 changes: 2 additions & 0 deletions native/cpp/compiler/DefinitionBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class DefinitionBuilder : public SemanticContextBuilder<Pol::Bscript::Compiler::
Pol::Bscript::Compiler::FunctionParameterDeclaration* param ) override;
virtual std::optional<Pol::Bscript::Compiler::SourceLocation> get_program(
const std::string& name, Pol::Bscript::Compiler::Program* program ) override;
virtual std::optional<Pol::Bscript::Compiler::SourceLocation> get_program_parameter(
const std::string& name ) override;
};

} // namespace VSCodeEscript::CompilerExt
Expand Down
287 changes: 287 additions & 0 deletions native/cpp/compiler/ReferencesBuilder.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
#include "ReferencesBuilder.h"

#include "../napi/LSPDocument.h"
#include "../napi/LSPWorkspace.h"
#include "bscript/compiler/ast/FunctionCall.h"
#include "bscript/compiler/ast/Identifier.h"
#include "bscript/compiler/ast/UserFunction.h"
#include "bscript/compiler/file/SourceFileIdentifier.h"
#include "bscript/compiler/model/FunctionLink.h"
#include <filesystem>

namespace fs = std::filesystem;
using namespace Pol::Bscript::Compiler;

namespace VSCodeEscript::CompilerExt
{

bool source_location_equal( const SourceLocation& a, const SourceLocation& b )
{
return a.range.start.line_number == b.range.start.line_number &&
a.range.start.character_column == b.range.start.character_column &&
a.range.end.line_number == b.range.end.line_number &&
a.range.end.character_column == b.range.end.character_column &&
stricmp( a.source_file_identifier->pathname.c_str(),
b.source_file_identifier->pathname.c_str() ) == 0;
}

bool SourceLocationComparator::operator()( const SourceLocation& x1,
const SourceLocation& x2 ) const
{
if ( x1.range.start.line_number != x2.range.start.line_number )
{
return x1.range.start.line_number < x2.range.start.line_number;
}
if ( x1.range.start.character_column != x2.range.start.character_column )
{
return x1.range.start.character_column < x2.range.start.character_column;
}
if ( x1.range.start.token_index != x2.range.start.token_index )
{
return x1.range.start.token_index < x2.range.start.token_index;
}

if ( x1.range.end.line_number != x2.range.end.line_number )
{
return x1.range.end.line_number < x2.range.end.line_number;
}
if ( x1.range.end.character_column != x2.range.end.character_column )
{
return x1.range.end.character_column < x2.range.end.character_column;
}
if ( x1.range.end.token_index != x2.range.end.token_index )
{
return x1.range.end.token_index < x2.range.end.token_index;
}

// Compare source file identifiers
auto compare = stricmp( x1.source_file_identifier->pathname.c_str(),
x2.source_file_identifier->pathname.c_str() );

if ( compare != 0 )
{
return compare < 0;
}

return false;
}

class GlobalIdentifierFinder : public NodeVisitor
{
public:
GlobalIdentifierFinder( ReferencesResult& results,
const SourceLocation& definition_source_location )
: results( results ), definition_source_location( definition_source_location )
{
}

void visit_identifier( Identifier& node ) override
{
if ( node.variable &&
source_location_equal( definition_source_location, node.variable->source_location ) )
{
results.emplace( node.source_location );
}

visit_children( node );
};

ReferencesResult& results;

const SourceLocation& definition_source_location;
};

class GlobalFunctionFinder : public NodeVisitor
{
public:
GlobalFunctionFinder( ReferencesResult& results, Function* function )
: results( results ), function( function )
{
}

template <typename T>
void add_if_matched( FunctionCall& node, T user_function_link )
{
if ( source_location_equal( user_function_link->source_location, function->source_location ) )
{
// We need to make a new location for only the method name, as FunctionCall source
// location includes the arguments

const auto& start = node.source_location.range.start;
Range r{ { start.line_number, start.character_column },
{ start.line_number, static_cast<unsigned short>( start.character_column +
node.method_name.length() ) } };

results.emplace( SourceLocation( node.source_location.source_file_identifier, r ) );
}
}

void visit_function_call( FunctionCall& node ) override
{
if ( auto link = node.function_link )
{
if ( auto user_function_link = link->user_function() )
{
add_if_matched( node, user_function_link );
}
else if ( auto module_function_decl = link->module_function_declaration() )
{
add_if_matched( node, module_function_decl );
}
}

// for includes, the children of function calls are empty...?
for ( auto& child : node.children )
{
if ( child )
{
child->accept( *this );
}
}
};

ReferencesResult& results;

const Function* function;
};

class GlobalConstantFinder : public NodeVisitor
{
public:
GlobalConstantFinder( ReferencesResult& results, ConstDeclaration* const_decl )
: results( results ), const_decl( const_decl )
{
}

void visit_children( Node& node )
{
for ( auto& child : node.children )
{
if ( !child )
{
continue;
}

if ( child->unoptimized_node )
{
if ( auto identifier = dynamic_cast<Identifier*>( child->unoptimized_node.get() ) )
{
if ( identifier->name == const_decl->identifier )
{
results.emplace( identifier->source_location );
}
}
}
child->accept( *this );
}
}

ReferencesResult& results;

ConstDeclaration* const_decl;
};

ReferencesBuilder::ReferencesBuilder( CompilerWorkspace& workspace, LSPWorkspace* lsp_workspace,
const Position& position )
: SemanticContextBuilder( workspace, position ), lsp_workspace( lsp_workspace )
{
}

std::optional<ReferencesResult> ReferencesBuilder::get_variable(
std::shared_ptr<Variable> variable )
{
auto ext = fs::path( variable->source_location.source_file_identifier->pathname ).extension();
auto is_source = !ext.compare( ".src" );

ReferencesResult results;
GlobalIdentifierFinder finder( results, variable->source_location );
if ( is_source )
{
workspace.accept( finder );
}
else
{
lsp_workspace->foreach_cache_entry( [&]( LSPDocument* document )
{ document->accept_visitor( finder ); } );
}
return results;
}


std::optional<ReferencesResult> ReferencesBuilder::get_constant( ConstDeclaration* const_decl )
{
auto ext = fs::path( const_decl->source_location.source_file_identifier->pathname ).extension();
auto is_source = !ext.compare( ".src" );

ReferencesResult results;
GlobalConstantFinder finder( results, const_decl );
if ( is_source )
{
workspace.accept( finder );
}
else
{
lsp_workspace->foreach_cache_entry( [&]( LSPDocument* document )
{ document->accept_visitor( finder ); } );
}
return results;
}

std::optional<ReferencesResult> ReferencesBuilder::get_module_function(
ModuleFunctionDeclaration* funct )
{
ReferencesResult results;
GlobalFunctionFinder finder( results, funct );
lsp_workspace->foreach_cache_entry( [&]( LSPDocument* document )
{ document->accept_visitor( finder ); } );
return results;
}

std::optional<ReferencesResult> ReferencesBuilder::get_program_parameter( const std::string& name )
{
ReferencesResult results;
if ( auto& program = workspace.program )
{
for ( auto& child : program->parameter_list().children )
{
auto& program_parameter = static_cast<ProgramParameterDeclaration&>( *child );
if ( program_parameter.name == name )
{
GlobalIdentifierFinder finder( results, program_parameter.source_location );
finder.visit_function_body( program->body() );
return results;
}
}
}
return results;
}

std::optional<ReferencesResult> ReferencesBuilder::get_user_function_parameter(
UserFunction* function_def, FunctionParameterDeclaration* param )
{
ReferencesResult results;
GlobalIdentifierFinder finder( results, param->source_location );
workspace.accept( finder );
return results;
}

std::optional<ReferencesResult> ReferencesBuilder::get_user_function( UserFunction* funct )
{
auto ext = fs::path( funct->source_location.source_file_identifier->pathname ).extension();
auto is_source = !ext.compare( ".src" );

ReferencesResult results;
GlobalFunctionFinder finder( results, funct );
if ( is_source )
{
workspace.accept( finder );
}
else
{
lsp_workspace->foreach_cache_entry( [&]( LSPDocument* document )
{ document->accept_visitor( finder ); } );
}
return results;
}

} // namespace VSCodeEscript::CompilerExt
Loading

0 comments on commit c3d2a48

Please sign in to comment.