[Mlir-commits] [mlir] 41d2c6d - [mlir][PDLL-LSP] Add code completion for include file paths

River Riddle llvmlistbot at llvm.org
Tue Apr 26 18:34:21 PDT 2022


Author: River Riddle
Date: 2022-04-26T18:33:17-07:00
New Revision: 41d2c6df5cce4a4d65ff2956be762a50600ebfd9

URL: https://github.com/llvm/llvm-project/commit/41d2c6df5cce4a4d65ff2956be762a50600ebfd9
DIFF: https://github.com/llvm/llvm-project/commit/41d2c6df5cce4a4d65ff2956be762a50600ebfd9.diff

LOG: [mlir][PDLL-LSP] Add code completion for include file paths

This allows for providing completion results for include directive
file paths by searching the set of include directories for the current
file.

Differential Revision: https://reviews.llvm.org/D124112

Added: 
    

Modified: 
    mlir/include/mlir/Tools/PDLL/Parser/CodeComplete.h
    mlir/lib/Tools/PDLL/Parser/Lexer.cpp
    mlir/lib/Tools/PDLL/Parser/Lexer.h
    mlir/lib/Tools/PDLL/Parser/Parser.cpp
    mlir/lib/Tools/mlir-pdll-lsp-server/LSPServer.cpp
    mlir/lib/Tools/mlir-pdll-lsp-server/PDLLServer.cpp
    mlir/test/mlir-pdll-lsp-server/completion.test

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/Tools/PDLL/Parser/CodeComplete.h b/mlir/include/mlir/Tools/PDLL/Parser/CodeComplete.h
index 7867bda9a3a3a..d0ccbe97e4eac 100644
--- a/mlir/include/mlir/Tools/PDLL/Parser/CodeComplete.h
+++ b/mlir/include/mlir/Tools/PDLL/Parser/CodeComplete.h
@@ -66,6 +66,9 @@ class CodeCompleteContext {
   /// Signal code completion for Pattern metadata.
   virtual void codeCompletePatternMetadata() {}
 
+  /// Signal code completion for an include filename.
+  virtual void codeCompleteIncludeFilename(StringRef curPath) {}
+
   //===--------------------------------------------------------------------===//
   // Signature Hooks
   //===--------------------------------------------------------------------===//

diff  --git a/mlir/lib/Tools/PDLL/Parser/Lexer.cpp b/mlir/lib/Tools/PDLL/Parser/Lexer.cpp
index 9035f5c620d33..6b338f1d227d7 100644
--- a/mlir/lib/Tools/PDLL/Parser/Lexer.cpp
+++ b/mlir/lib/Tools/PDLL/Parser/Lexer.cpp
@@ -22,11 +22,15 @@ using namespace mlir::pdll;
 //===----------------------------------------------------------------------===//
 
 std::string Token::getStringValue() const {
-  assert(getKind() == string || getKind() == string_block);
+  assert(getKind() == string || getKind() == string_block ||
+         getKind() == code_complete_string);
 
   // Start by dropping the quotes.
-  StringRef bytes = getSpelling().drop_front().drop_back();
-  if (is(string_block)) bytes = bytes.drop_front().drop_back();
+  StringRef bytes = getSpelling();
+  if (is(string))
+    bytes = bytes.drop_front().drop_back();
+  else if (is(string_block))
+    bytes = bytes.drop_front(2).drop_back(2);
 
   std::string result;
   result.reserve(bytes.size());
@@ -337,6 +341,16 @@ Token Lexer::lexNumber(const char *tokStart) {
 
 Token Lexer::lexString(const char *tokStart, bool isStringBlock) {
   while (true) {
+    // Check to see if there is a code completion location within the string. In
+    // these cases we generate a completion location and place the currently
+    // lexed string within the token (without the quotes). This allows for the
+    // parser to use the partially lexed string when computing the completion
+    // results.
+    if (curPtr == codeCompletionLocation) {
+      return formToken(Token::code_complete_string,
+                       tokStart + (isStringBlock ? 2 : 1));
+    }
+
     switch (*curPtr++) {
       case '"':
         // If this is a string block, we only end the string when we encounter a

diff  --git a/mlir/lib/Tools/PDLL/Parser/Lexer.h b/mlir/lib/Tools/PDLL/Parser/Lexer.h
index c2d9823dfd2bb..1c521f37bce33 100644
--- a/mlir/lib/Tools/PDLL/Parser/Lexer.h
+++ b/mlir/lib/Tools/PDLL/Parser/Lexer.h
@@ -34,22 +34,25 @@ class DiagnosticEngine;
 class Token {
 public:
   enum Kind {
-    // Markers.
+    /// Markers.
     eof,
     error,
+    /// Token signifying a code completion location.
     code_complete,
+    /// Token signifying a code completion location within a string.
+    code_complete_string,
 
-    // Keywords.
+    /// Keywords.
     KW_BEGIN,
-    // Dependent keywords, i.e. those that are treated as keywords depending on
-    // the current parser context.
+    /// Dependent keywords, i.e. those that are treated as keywords depending on
+    /// the current parser context.
     KW_DEPENDENT_BEGIN,
     kw_attr,
     kw_op,
     kw_type,
     KW_DEPENDENT_END,
 
-    // General keywords.
+    /// General keywords.
     kw_Attr,
     kw_erase,
     kw_let,
@@ -68,7 +71,7 @@ class Token {
     kw_with,
     KW_END,
 
-    // Punctuation.
+    /// Punctuation.
     arrow,
     colon,
     comma,
@@ -76,7 +79,7 @@ class Token {
     equal,
     equal_arrow,
     semicolon,
-    // Paired punctuation.
+    /// Paired punctuation.
     less,
     greater,
     l_brace,
@@ -87,7 +90,7 @@ class Token {
     r_square,
     underscore,
 
-    // Tokens.
+    /// Tokens.
     directive,
     identifier,
     integer,

diff  --git a/mlir/lib/Tools/PDLL/Parser/Parser.cpp b/mlir/lib/Tools/PDLL/Parser/Parser.cpp
index dc58e4e25137c..3397b30a583e9 100644
--- a/mlir/lib/Tools/PDLL/Parser/Parser.cpp
+++ b/mlir/lib/Tools/PDLL/Parser/Parser.cpp
@@ -424,6 +424,7 @@ class Parser {
   LogicalResult codeCompleteDialectName();
   LogicalResult codeCompleteOperationName(StringRef dialectName);
   LogicalResult codeCompletePatternMetadata();
+  LogicalResult codeCompleteIncludeFilename(StringRef curPath);
 
   void codeCompleteCallSignature(ast::Node *parent, unsigned currentNumArgs);
   void codeCompleteOperationOperandsSignature(Optional<StringRef> opName,
@@ -680,6 +681,10 @@ LogicalResult Parser::parseInclude(SmallVectorImpl<ast::Decl *> &decls) {
   SMRange loc = curToken.getLoc();
   consumeToken(Token::directive);
 
+  // Handle code completion of the include file path.
+  if (curToken.is(Token::code_complete_string))
+    return codeCompleteIncludeFilename(curToken.getStringValue());
+
   // Parse the file being included.
   if (!curToken.isString())
     return emitError(loc,
@@ -2922,6 +2927,11 @@ LogicalResult Parser::codeCompletePatternMetadata() {
   return failure();
 }
 
+LogicalResult Parser::codeCompleteIncludeFilename(StringRef curPath) {
+  codeCompleteContext->codeCompleteIncludeFilename(curPath);
+  return failure();
+}
+
 void Parser::codeCompleteCallSignature(ast::Node *parent,
                                        unsigned currentNumArgs) {
   ast::CallableDecl *callableDecl = tryExtractCallableDecl(parent);

diff  --git a/mlir/lib/Tools/mlir-pdll-lsp-server/LSPServer.cpp b/mlir/lib/Tools/mlir-pdll-lsp-server/LSPServer.cpp
index 48930cff57b0f..311f6042ae836 100644
--- a/mlir/lib/Tools/mlir-pdll-lsp-server/LSPServer.cpp
+++ b/mlir/lib/Tools/mlir-pdll-lsp-server/LSPServer.cpp
@@ -119,7 +119,8 @@ void LSPServer::onInitialize(const InitializeParams &params,
              ">", ":",  ";", ",", "+", "-", "/",  "*", "%",
              "^", "&",  "#", "?", ".", "=", "\"", "'", "|"}},
            {"resolveProvider", false},
-           {"triggerCharacters", {".", ">", "(", "{", ",", "<", ":", "[", " "}},
+           {"triggerCharacters",
+            {".", ">", "(", "{", ",", "<", ":", "[", " ", "\"", "/"}},
        }},
       {"signatureHelpProvider",
        llvm::json::Object{

diff  --git a/mlir/lib/Tools/mlir-pdll-lsp-server/PDLLServer.cpp b/mlir/lib/Tools/mlir-pdll-lsp-server/PDLLServer.cpp
index e321e537b7735..1fd00f7db4c8b 100644
--- a/mlir/lib/Tools/mlir-pdll-lsp-server/PDLLServer.cpp
+++ b/mlir/lib/Tools/mlir-pdll-lsp-server/PDLLServer.cpp
@@ -22,7 +22,9 @@
 #include "mlir/Tools/PDLL/Parser/Parser.h"
 #include "llvm/ADT/IntervalMap.h"
 #include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringSet.h"
 #include "llvm/ADT/TypeSwitch.h"
+#include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Path.h"
 
 using namespace mlir;
@@ -665,9 +667,10 @@ namespace {
 class LSPCodeCompleteContext : public CodeCompleteContext {
 public:
   LSPCodeCompleteContext(SMLoc completeLoc, lsp::CompletionList &completionList,
-                         ods::Context &odsContext)
+                         ods::Context &odsContext,
+                         ArrayRef<std::string> includeDirs)
       : CodeCompleteContext(completeLoc), completionList(completionList),
-        odsContext(odsContext) {}
+        odsContext(odsContext), includeDirs(includeDirs) {}
 
   void codeCompleteTupleMemberAccess(ast::TupleType tupleType) final {
     ArrayRef<ast::Type> elementTypes = tupleType.getElementTypes();
@@ -901,9 +904,68 @@ class LSPCodeCompleteContext : public CodeCompleteContext {
                         "The pattern properly handles recursive application.");
   }
 
+  void codeCompleteIncludeFilename(StringRef curPath) final {
+    // Normalize the path to allow for interacting with the file system
+    // utilities.
+    SmallString<128> nativeRelDir(llvm::sys::path::convert_to_slash(curPath));
+    llvm::sys::path::native(nativeRelDir);
+
+    // Set of already included completion paths.
+    StringSet<> seenResults;
+
+    // Functor used to add a single include completion item.
+    auto addIncludeCompletion = [&](StringRef path, bool isDirectory) {
+      lsp::CompletionItem item;
+      item.label = (path + (isDirectory ? "/" : "")).str();
+      item.kind = isDirectory ? lsp::CompletionItemKind::Folder
+                              : lsp::CompletionItemKind::File;
+      if (seenResults.insert(item.label).second)
+        completionList.items.emplace_back(item);
+    };
+
+    // Process the include directories for this file, adding any potential
+    // nested include files or directories.
+    for (StringRef includeDir : includeDirs) {
+      llvm::SmallString<128> dir = includeDir;
+      if (!nativeRelDir.empty())
+        llvm::sys::path::append(dir, nativeRelDir);
+
+      std::error_code errorCode;
+      for (auto it = llvm::sys::fs::directory_iterator(dir, errorCode),
+                e = llvm::sys::fs::directory_iterator();
+           !errorCode && it != e; it.increment(errorCode)) {
+        StringRef filename = llvm::sys::path::filename(it->path());
+
+        // To know whether a symlink should be treated as file or a directory,
+        // we have to stat it. This should be cheap enough as there shouldn't be
+        // many symlinks.
+        llvm::sys::fs::file_type fileType = it->type();
+        if (fileType == llvm::sys::fs::file_type::symlink_file) {
+          if (auto fileStatus = it->status())
+            fileType = fileStatus->type();
+        }
+
+        switch (fileType) {
+        case llvm::sys::fs::file_type::directory_file:
+          addIncludeCompletion(filename, /*isDirectory=*/true);
+          break;
+        case llvm::sys::fs::file_type::regular_file: {
+          // Only consider concrete files that can actually be included by PDLL.
+          if (filename.endswith(".pdll") || filename.endswith(".td"))
+            addIncludeCompletion(filename, /*isDirectory=*/false);
+          break;
+        }
+        default:
+          break;
+        }
+      }
+    };
+  }
+
 private:
   lsp::CompletionList &completionList;
   ods::Context &odsContext;
+  ArrayRef<std::string> includeDirs;
 };
 } // namespace
 
@@ -921,8 +983,8 @@ PDLDocument::getCodeCompletion(const lsp::URIForFile &uri,
   // code completion context provided.
   ods::Context tmpODSContext;
   lsp::CompletionList completionList;
-  LSPCodeCompleteContext lspCompleteContext(posLoc, completionList,
-                                            tmpODSContext);
+  LSPCodeCompleteContext lspCompleteContext(
+      posLoc, completionList, tmpODSContext, sourceMgr.getIncludeDirs());
 
   ast::Context tmpContext(tmpODSContext);
   (void)parsePDLAST(tmpContext, sourceMgr, &lspCompleteContext);

diff  --git a/mlir/test/mlir-pdll-lsp-server/completion.test b/mlir/test/mlir-pdll-lsp-server/completion.test
index a8d80d8f5ff59..b682fa5d15f5b 100644
--- a/mlir/test/mlir-pdll-lsp-server/completion.test
+++ b/mlir/test/mlir-pdll-lsp-server/completion.test
@@ -1,16 +1,16 @@
-// RUN: mlir-pdll-lsp-server -lit-test < %s | FileCheck -strict-whitespace %s
+// RUN: mlir-pdll-lsp-server -pdll-extra-dir %S -pdll-extra-dir %S/../../include  -lit-test < %s | FileCheck -strict-whitespace %s
 {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"pdll","capabilities":{},"trace":"off"}}
 // -----
 {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{
   "uri":"test:///foo.pdll",
   "languageId":"pdll",
   "version":1,
-  "text":"Constraint ValueCst(value: Value);\nConstraint Cst();\nPattern FooPattern with benefit(1) {\nlet tuple = (value1 = _: Op, _: Op<test.op>);\nerase tuple.value1;\n}"
+  "text":"#include \"include/included.pdll\"\nConstraint ValueCst(value: Value);\nConstraint Cst();\nPattern FooPattern with benefit(1) {\nlet tuple = (value1 = _: Op, _: Op<test.op>);\nerase tuple.value1;\n}"
 }}}
 // -----
 {"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{
   "textDocument":{"uri":"test:///foo.pdll"},
-  "position":{"line":4,"character":12}
+  "position":{"line":5,"character":12}
 }}
 //      CHECK:  "id": 1
 // CHECK-NEXT:  "jsonrpc": "2.0",
@@ -49,7 +49,7 @@
 // -----
 {"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{
   "textDocument":{"uri":"test:///foo.pdll"},
-  "position":{"line":2,"character":23}
+  "position":{"line":3,"character":23}
 }}
 //      CHECK:  "id": 1
 // CHECK-NEXT:  "jsonrpc": "2.0",
@@ -82,7 +82,7 @@
 // -----
 {"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{
   "textDocument":{"uri":"test:///foo.pdll"},
-  "position":{"line":3,"character":24}
+  "position":{"line":4,"character":24}
 }}
 //      CHECK:  "id": 1
 // CHECK-NEXT:  "jsonrpc": "2.0",
@@ -200,6 +200,26 @@
 // CHECK-NEXT:    ]
 // CHECK-NEXT:  }
 // -----
+{"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{
+  "textDocument":{"uri":"test:///foo.pdll"},
+  "position":{"line":0,"character":18}
+}}
+//      CHECK:  "id": 1
+// CHECK-NEXT:  "jsonrpc": "2.0",
+// CHECK-NEXT:  "result": {
+// CHECK-NEXT:    "isIncomplete": false,
+// CHECK-NEXT:    "items": [
+// CHECK-NEXT:      {
+// CHECK-NEXT:        "kind": 17,
+// CHECK-NEXT:        "label": "included.td"
+// CHECK-NEXT:      },
+// CHECK-NEXT:      {
+// CHECK-NEXT:        "kind": 17,
+// CHECK-NEXT:        "label": "included.pdll"
+// CHECK-NEXT:      }
+// CHECK-NEXT:    ]
+// CHECK-NEXT:  }
+// -----
 {"jsonrpc":"2.0","id":3,"method":"shutdown"}
 // -----
 {"jsonrpc":"2.0","method":"exit"}


        


More information about the Mlir-commits mailing list