diff --git a/.clang-format b/.clang-format index 81e4f0e..2160f52 100644 --- a/.clang-format +++ b/.clang-format @@ -41,7 +41,6 @@ BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true ColumnLimit: 80 CommentPragmas: '^ IWYU pragma:' -BreakBeforeInheritanceComma: false ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 @@ -49,7 +48,6 @@ Cpp11BracedListStyle: true DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false -FixNamespaceComments: true ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] IncludeCategories: - Regex: '^"(llvm|llvm-c|clang|clang-c)/' @@ -62,8 +60,6 @@ IncludeIsMainRegex: '$' IndentCaseLabels: false IndentWidth: 2 IndentWrappedFunctionNames: false -JavaScriptQuotes: Leave -JavaScriptWrapImports: true KeepEmptyLinesAtTheStartOfBlocks: true MacroBlockBegin: '' MacroBlockEnd: '' @@ -82,7 +78,6 @@ PointerAlignment: Right ReflowComments: true SortIncludes: true SpaceAfterCStyleCast: false -SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false diff --git a/APIIntegrationTests.cpp b/APIIntegrationTests.cpp index b1712f2..e4fd81e 100644 --- a/APIIntegrationTests.cpp +++ b/APIIntegrationTests.cpp @@ -158,6 +158,25 @@ class IntegrationTestSuite { assert(res.status == 200); } + void testSuccessfulCursorInfo() { + auto exampleDir = GetExamplesDir(); + auto exampleName = exampleDir + std::string("some_swift.swift"); + auto example = ReadFile(exampleName); + std::vector flags; + flags.push_back("-sdk"); + flags.push_back("/Applications/Xcode.app/Contents/Developer/Platforms/" + "MacOSX.platform/Developer/SDKs/MacOSX.sdk"); + flags.push_back("-target"); + flags.push_back("x86_64-apple-macosx10.12"); + + using namespace ssvim::ResultStatus; + auto body = MakeCompletionPostBody(19, 15, exampleName, example, flags); + auto responseValue = PostRequest(_boundPort, "/cursorinfo", body); + auto res = Get>(responseValue); + assert(res.body.length() > 0); + assert(res.status == 200); + } + void testStatus() { using namespace ssvim::ResultStatus; auto responseValue = PostRequest(_boundPort, "/status", ""); @@ -207,8 +226,10 @@ int main(int, char const *[]) { auto startCmd = std::string("`./build/http_server"); startCmd += " --port "; startCmd += boundPort; + startCmd += " --log DEBUG"; startCmd += " >/dev/null`&"; + // Startup the service int started = system(startCmd.c_str()); assert(started == 0 && "Failed to start"); @@ -225,6 +246,8 @@ int main(int, char const *[]) { std::cout << "testSuccessfulCompletion" << std::endl; suite.testSuccessfulCompletion(); + std::cout << "testSuccessfulCursorInfo" << std::endl; + suite.testSuccessfulCursorInfo(); // TODO: // std::cout << "testRunningAfterGarbageJSON" << std::endl; diff --git a/IntegrationTestResponses/cursorinfo.json b/IntegrationTestResponses/cursorinfo.json new file mode 100644 index 0000000..1439085 --- /dev/null +++ b/IntegrationTestResponses/cursorinfo.json @@ -0,0 +1,13 @@ +{ + key.kind: source.lang.swift.ref.var.local, + key.name: "self", + key.usr: "s:10some_swift7MySwiftC15anotherFunctionyyF4selfL_ACvp", + key.filepath: "/Users/jerrymarino/.vim/bundle/iCompleteMe/third_party/icmd/third_party/swiftyswiftvim/Examples/some_swift.swift", + key.offset: 357, + key.length: 4, + key.typename: "MySwift", + key.annotated_decl: "let `self`: MySwift", + key.fully_annotated_decl: "let `self`: MySwift", + key.typeusr: "_T010some_swift7MySwiftCD", + key.parent_loc: 357 +} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f262fff --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +integration-test: + ./bootstrap + ./build/integration_tests + +# Mainly used for manually exploring sourcekit things +experiment: + ./bootstrap + ./build/test_driver + +# Consider expressing this better. +format: + @$(shell for f in $$(ls *.{hpp,cpp}); do clang-format $$f; done) diff --git a/SemanticHTTPServer.cpp b/SemanticHTTPServer.cpp index 9b81983..6f8d4ff 100644 --- a/SemanticHTTPServer.cpp +++ b/SemanticHTTPServer.cpp @@ -68,6 +68,7 @@ EndpointImpl makeSlowTestEndpoint(); EndpointImpl makeStatusEndpoint(); EndpointImpl makeShutdownEndpoint(); EndpointImpl makeCompletionsEndpoint(); +EndpointImpl makeCursorInfoEndpoint(); EndpointImpl makeDiagnosticsEndpoint(); response notFoundResponse(req_type request); @@ -104,6 +105,7 @@ class Session : public std::enable_shared_from_this { insert_endpoint("/status", makeStatusEndpoint()); insert_endpoint("/shutdown", makeShutdownEndpoint()); insert_endpoint("/completions", makeCompletionsEndpoint()); + insert_endpoint("/cursorinfo", makeCursorInfoEndpoint()); insert_endpoint("/diagnostics", makeDiagnosticsEndpoint()); insert_endpoint("/slow_test", makeSlowTestEndpoint()); } @@ -332,6 +334,53 @@ EndpointImpl makeCompletionsEndpoint() { }); } +EndpointImpl makeCursorInfoEndpoint() { + return EndpointImpl([&](std::shared_ptr session) { + // Parse in data + auto logger = session->logger(); + auto bodyString = session->request().body; + logger << bodyString; + auto bodyJSON = readJSONPostBody(bodyString); + + auto fileName = bodyJSON.get("file_name"); + auto column = bodyJSON.get("column"); + auto line = bodyJSON.get("line"); + auto contents = bodyJSON.get("contents"); + auto flags = as_vector(bodyJSON, "flags"); + logger << "file_name:" << fileName; + logger << "column:" << column; + logger << "line:" << line; + for (auto &f : flags) { + logger << "flags:" << f; + } + + using namespace ssvim; + SwiftCompleter completer(session->logger().level()); + + auto files = std::vector(); + auto unsaved = UnsavedFile(); + unsaved.contents = contents; + unsaved.fileName = fileName; + files.push_back(unsaved); + + logger << "SEND_REQ"; + auto candidates = completer.CursorInfoForLocationInFile( + fileName, line, column, files, flags); + + logger << "GOT_RES"; + session->logger().log(LogLevelExtreme, candidates); + // Build out response + response res; + res.status = 200; + res.version = session->request().version; + res.fields.insert(HeaderKeyServer, HeaderValueServer); + res.fields.insert(HeaderKeyContentType, HeaderValueContentTypeJSON); + res.body = candidates; + prepare(res); + session->write(res); + }); +} + // Make completions endpoint returns an endpoint that // handles basic completion requests // diff --git a/SwiftCompleter.cpp b/SwiftCompleter.cpp index dc1f20c..1e6683a 100644 --- a/SwiftCompleter.cpp +++ b/SwiftCompleter.cpp @@ -105,9 +105,8 @@ struct CompletionContext { std::vector DefaultOSXArgs() { return { - "-sdk", - "/Applications/Xcode.app/Contents/Developer/Platforms/" - "MacOSX.platform/Developer/SDKs/MacOSX.sdk", + "-sdk", "/Applications/Xcode.app/Contents/Developer/Platforms/" + "MacOSX.platform/Developer/SDKs/MacOSX.sdk", "-target", "x86_64-apple-macosx10.12", }; } @@ -122,6 +121,7 @@ class SourceKitService { int CompletionOpen(CompletionContext &ctx, char **oresponse); int EditorOpen(CompletionContext &ctx, char **oresponse); int EditorReplaceText(CompletionContext &ctx, char **oresponse); + int GetCursorInfo(CompletionContext &ctx, char **oresponse); }; } // namespace ssvim @@ -269,8 +269,8 @@ using namespace ssvim; // // This seemed necessary on Swift V2 when it was first written, but hopefully // it can be improved. -static void GetOffset(CompletionContext &ctx, unsigned *offset, - std::string *CleanFile) { +static void GetCompletionOffset(CompletionContext &ctx, unsigned *offset, + std::string *CleanFile) { auto line = ctx.line; auto column = ctx.column; auto fileName = ctx.sourceFilename; @@ -319,6 +319,36 @@ static void GetOffset(CompletionContext &ctx, unsigned *offset, } } +// Get the offset for a given column, line +static void GetOffset(CompletionContext &ctx, unsigned *offset) { + auto line = ctx.line; + auto fileName = ctx.sourceFilename; + std::string unsavedInput; + + for (auto unsavedFile : ctx.unsavedFiles) { + if (unsavedFile.fileName == fileName) { + unsavedInput = unsavedFile.contents; + break; + } + } + + assert(unsavedInput.length() && "Missing unsaved file"); + + std::istringstream sourceFile(unsavedInput); + std::string someLine; + unsigned ct = 0; + unsigned currentLine = 0; + while (std::getline(sourceFile, someLine)) { + ct += someLine.length(); + if (currentLine == line) { + *offset = ct; + return; + } else { + currentLine++; + } + } +} + SourceKitService::SourceKitService(ssvim::LogLevel logLevel) : _logger(logLevel, "SKT") { // Initialize SourceKitD resource @@ -352,7 +382,7 @@ int SourceKitService::CompletionUpdate(CompletionContext &ctx, sourcekitd_uid_get_from_cstr("source.request.codecomplete.update"); unsigned CodeCompletionOffset = 0; std::string CleanFile; - GetOffset(ctx, &CodeCompletionOffset, &CleanFile); + GetCompletionOffset(ctx, &CodeCompletionOffset, &CleanFile); bool isError = CodeCompleteRequest( RequestCodeCompleteUpdate, ctx.sourceFilename.data(), @@ -376,7 +406,7 @@ int SourceKitService::CompletionOpen(CompletionContext &ctx, char **oresponse) { sourcekitd_uid_get_from_cstr("source.request.codecomplete.open"); unsigned CodeCompletionOffset = 0; std::string CleanFile; - GetOffset(ctx, &CodeCompletionOffset, &CleanFile); + GetCompletionOffset(ctx, &CodeCompletionOffset, &CleanFile); bool isError = CodeCompleteRequest( RequestCodeCompleteOpen, ctx.sourceFilename.data(), CodeCompletionOffset, @@ -437,6 +467,41 @@ int SourceKitService::EditorReplaceText(CompletionContext &ctx, return isError; } +int SourceKitService::GetCursorInfo(CompletionContext &ctx, char **oresponse) { + std::cout << "WILL_DECL_OPEN"; + sourcekitd_uid_t RequestCursorInfo = + sourcekitd_uid_get_from_cstr("source.request.cursorinfo"); + unsigned Offset = 0; + GetOffset(ctx, &Offset); + + auto fileName = ctx.sourceFilename; + std::string unsavedInput; + for (auto unsavedFile : ctx.unsavedFiles) { + if (unsavedFile.fileName == fileName) { + unsavedInput = unsavedFile.contents; + break; + } + } + + bool isError = CodeCompleteRequest( + RequestCursorInfo, ctx.sourceFilename.data(), Offset, + unsavedInput.c_str(), ctx.compilerArgs(), nullptr, + [&](sourcekitd_object_t response) -> bool { + if (sourcekitd_response_is_error(response)) { + return true; + } + *oresponse = PrintResponse(response); + _logger.log(LogLevelExtreme, *oresponse); + return false; + }); + + if (isError) { + _logger << "DID_DECL_OPEN_IS_ERROR"; + } + std::cout << "DID_DECL_OPEN"; + return isError; +} + #pragma mark - SwiftCompleter namespace ssvim { @@ -520,4 +585,29 @@ SwiftCompleter::DiagnosticsForFile(const std::string &filename, auto semaresult = future.get(); return semaresult; } + +const std::string SwiftCompleter::CursorInfoForLocationInFile(const std::string &filename, + int line, + int column, + const std::vector &unsavedFiles, + const std::vector &flags) { + CompletionContext ctx; + ctx.sourceFilename = filename; + ctx.line = line; + ctx.column = column; + ctx.unsavedFiles = unsavedFiles; + ctx.flags = flags; + + SourceKitService sktService(_logger.level()); + char *response = NULL; + sktService.GetCursorInfo(ctx, &response); + if (response == NULL) { + // FIXME: Propagate SourceKitService Errors + static auto EmptyResponse = "{ 'key.results':[] }"; + _logger << "Empty response"; + return EmptyResponse; + } + + return response; +} } // namespace ssvim diff --git a/SwiftCompleter.hpp b/SwiftCompleter.hpp index 00bf42f..8d33ff2 100644 --- a/SwiftCompleter.hpp +++ b/SwiftCompleter.hpp @@ -38,5 +38,10 @@ class SwiftCompleter { DiagnosticsForFile(const std::string &filename, const std::vector &unsavedFiles, const std::vector &flags); + + const std::string + CursorInfoForLocationInFile(const std::string &filename, int line, int column, + const std::vector &unsavedFiles, + const std::vector &flags); }; } // namespace ssvim