aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/README.md41
-rw-r--r--.github/workflows/build_cmake.yml335
-rw-r--r--.gitignore2
-rw-r--r--CMakeLists.txt14
-rw-r--r--README.md40
-rw-r--r--haskell.pro7
-rw-r--r--plugins/haskell/CMakeLists.txt27
-rw-r--r--plugins/haskell/config.pri12
-rw-r--r--plugins/haskell/filecache.cpp139
-rw-r--r--plugins/haskell/filecache.h61
-rw-r--r--plugins/haskell/followsymbol.cpp141
-rw-r--r--plugins/haskell/followsymbol.h94
-rw-r--r--plugins/haskell/ghcmod.cpp408
-rw-r--r--plugins/haskell/ghcmod.h150
-rw-r--r--plugins/haskell/haskell.pro75
-rw-r--r--plugins/haskell/haskell.qbs7
-rw-r--r--plugins/haskell/haskellbuildconfiguration.cpp78
-rw-r--r--plugins/haskell/haskellbuildconfiguration.h10
-rw-r--r--plugins/haskell/haskellcompletionassist.cpp44
-rw-r--r--plugins/haskell/haskellcompletionassist.h45
-rw-r--r--plugins/haskell/haskellconstants.h3
-rw-r--r--plugins/haskell/haskelldocument.cpp51
-rw-r--r--plugins/haskell/haskelldocument.h51
-rw-r--r--plugins/haskell/haskelleditorfactory.cpp26
-rw-r--r--plugins/haskell/haskelleditorwidget.cpp113
-rw-r--r--plugins/haskell/haskelleditorwidget.h60
-rw-r--r--plugins/haskell/haskellhighlighter.cpp4
-rw-r--r--plugins/haskell/haskellhoverhandler.cpp166
-rw-r--r--plugins/haskell/haskellhoverhandler.h57
-rw-r--r--plugins/haskell/haskellmanager.cpp51
-rw-r--r--plugins/haskell/haskellmanager.h6
-rw-r--r--plugins/haskell/haskellplugin.cpp23
-rw-r--r--plugins/haskell/haskellproject.cpp67
-rw-r--r--plugins/haskell/haskellproject.h24
-rw-r--r--plugins/haskell/haskellrunconfiguration.cpp49
-rw-r--r--plugins/haskell/haskellrunconfiguration.h12
-rw-r--r--plugins/haskell/haskelltokenizer.cpp18
-rw-r--r--plugins/haskell/haskelltokenizer.h2
-rw-r--r--plugins/haskell/optionspage.cpp4
-rw-r--r--plugins/haskell/share/wizards/module/file.hs1
-rw-r--r--plugins/haskell/share/wizards/module/wizard.json42
-rw-r--r--plugins/haskell/stackbuildstep.cpp47
-rw-r--r--plugins/haskell/stackbuildstep.h7
-rw-r--r--tests/auto/auto.pro2
-rw-r--r--tests/auto/tokenizer/CMakeLists.txt8
-rw-r--r--tests/auto/tokenizer/tokenizer.pro11
46 files changed, 708 insertions, 1927 deletions
diff --git a/.github/workflows/README.md b/.github/workflows/README.md
new file mode 100644
index 0000000..107410c
--- /dev/null
+++ b/.github/workflows/README.md
@@ -0,0 +1,41 @@
+# GitHub Actions & Workflows
+
+The `build_cmake.yml` in this directory adds a [GitHub action][1] and workflow that builds
+your plugin anytime you push commits to GitHub on Windows, Linux and macOS.
+
+The build artifacts can be downloaded from GitHub and be installed into an existing Qt Creator
+installation.
+
+When you push a tag, the workflow also creates a new release on GitHub.
+
+## Keeping it up to date
+
+Near the top of the file you find a section starting with `env:`.
+
+The value for `QT_VERSION` specifies the Qt version to use for building the plugin.
+
+The value for `QT_CREATOR_VERSION` specifies the Qt Creator version to use for building the plugin.
+
+The value for `QT_CREATOR_SNAPSHOT` can either be `NO` or `latest` or the build ID of a specific
+snapshot build for the Qt Creator version that you specified.
+
+You need to keep these values updated for different versions of your plugin, and take care
+that the Qt version and Qt Creator version you specify are compatible.
+
+## What it does
+
+The build job consists of several steps:
+
+* Install required packages on the build host
+* Download, unpack and install the binary for the Qt version
+* Download and unpack the binary for the Qt Creator version
+* Build the plugin and upload the plugin libraries to GitHub
+* If a tag is pushed, create a release on GitHub for the tag, including zipped plugin libraries
+ for download
+
+## Limitations
+
+If your plugin requires additional resources besides the plugin library, you need to adapt the
+script accordingly.
+
+[1]: https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-github-actions
diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build_cmake.yml
new file mode 100644
index 0000000..21a889b
--- /dev/null
+++ b/.github/workflows/build_cmake.yml
@@ -0,0 +1,335 @@
+name: QMake Build Matrix
+
+on: [push]
+
+env:
+ PLUGIN_NAME: Haskell
+ QT_VERSION: 6.4.2
+ MACOS_DEPLOYMENT_TARGET: 10.14
+ QT_CREATOR_VERSION: 10.0.0-beta1
+ QT_CREATOR_SNAPSHOT: latest
+ CMAKE_VERSION: 3.21.1
+ NINJA_VERSION: 1.10.2
+
+jobs:
+ build:
+ name: ${{ matrix.config.name }}
+ runs-on: ${{ matrix.config.os }}
+ strategy:
+ matrix:
+ config:
+ - {
+ name: "Windows MSVC 2019", artifact: "Windows-x64",
+ os: windows-2019,
+ cc: "cl", cxx: "cl",
+ environment_script: "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build/vcvars64.bat",
+ }
+ - {
+ name: "Ubuntu Latest GCC", artifact: "Linux-x64",
+ os: ubuntu-latest,
+ cc: "gcc", cxx: "g++"
+ }
+ - {
+ name: "macOS Latest Clang", artifact: "macOS-x64",
+ os: macos-latest,
+ cc: "clang", cxx: "clang++"
+ }
+
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Download Ninja and CMake
+ shell: cmake -P {0}
+ run: |
+ set(cmake_version "$ENV{CMAKE_VERSION}")
+ set(ninja_version "$ENV{NINJA_VERSION}")
+
+ if ("${{ runner.os }}" STREQUAL "Windows")
+ set(ninja_suffix "win.zip")
+ set(cmake_suffix "windows-x86_64.zip")
+ set(cmake_dir "cmake-${cmake_version}-windows-x86_64/bin")
+ elseif ("${{ runner.os }}" STREQUAL "Linux")
+ set(ninja_suffix "linux.zip")
+ set(cmake_suffix "linux-x86_64.tar.gz")
+ set(cmake_dir "cmake-${cmake_version}-linux-x86_64/bin")
+ elseif ("${{ runner.os }}" STREQUAL "macOS")
+ set(ninja_suffix "mac.zip")
+ set(cmake_suffix "macos-universal.tar.gz")
+ set(cmake_dir "cmake-${cmake_version}-macos-universal/CMake.app/Contents/bin")
+ endif()
+
+ set(ninja_url "https://github.com/ninja-build/ninja/releases/download/v${ninja_version}/ninja-${ninja_suffix}")
+ file(DOWNLOAD "${ninja_url}" ./ninja.zip SHOW_PROGRESS)
+ execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ./ninja.zip)
+
+ set(cmake_url "https://github.com/Kitware/CMake/releases/download/v${cmake_version}/cmake-${cmake_version}-${cmake_suffix}")
+ file(DOWNLOAD "${cmake_url}" ./cmake.zip SHOW_PROGRESS)
+ execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ./cmake.zip)
+
+ # Add to PATH environment variable
+ file(TO_CMAKE_PATH "$ENV{GITHUB_WORKSPACE}/${cmake_dir}" cmake_dir)
+ set(path_separator ":")
+ if ("${{ runner.os }}" STREQUAL "Windows")
+ set(path_separator ";")
+ endif()
+ file(APPEND "$ENV{GITHUB_PATH}" "$ENV{GITHUB_WORKSPACE}${path_separator}${cmake_dir}")
+
+ if (NOT "${{ runner.os }}" STREQUAL "Windows")
+ execute_process(
+ COMMAND chmod +x ninja
+ COMMAND chmod +x ${cmake_dir}/cmake
+ )
+ endif()
+
+ - name: Install system libs
+ shell: cmake -P {0}
+ run: |
+ if ("${{ runner.os }}" STREQUAL "Linux")
+ execute_process(
+ COMMAND sudo apt update
+ )
+ execute_process(
+ COMMAND sudo apt install libgl1-mesa-dev
+ RESULT_VARIABLE result
+ )
+ if (NOT result EQUAL 0)
+ message(FATAL_ERROR "Failed to install dependencies")
+ endif()
+ endif()
+
+ - name: Download Qt
+ id: qt
+ shell: cmake -P {0}
+ run: |
+ set(qt_version "$ENV{QT_VERSION}")
+
+ string(REPLACE "." "" qt_version_dotless "${qt_version}")
+ if ("${{ runner.os }}" STREQUAL "Windows")
+ set(url_os "windows_x86")
+ set(qt_package_arch_suffix "win64_msvc2019_64")
+ set(qt_dir_prefix "${qt_version}/msvc2019_64")
+ set(qt_package_suffix "-Windows-Windows_10_21H2-MSVC2019-Windows-Windows_10_21H2-X86_64")
+ elseif ("${{ runner.os }}" STREQUAL "Linux")
+ set(url_os "linux_x64")
+ set(qt_package_arch_suffix "gcc_64")
+ set(qt_dir_prefix "${qt_version}/gcc_64")
+ set(qt_package_suffix "-Linux-RHEL_8_4-GCC-Linux-RHEL_8_4-X86_64")
+ elseif ("${{ runner.os }}" STREQUAL "macOS")
+ set(url_os "mac_x64")
+ set(qt_package_arch_suffix "clang_64")
+ set(qt_dir_prefix "${qt_version}/macos")
+ set(qt_package_suffix "-MacOS-MacOS_12-Clang-MacOS-MacOS_12-X86_64-ARM64")
+ endif()
+
+ set(qt_base_url "https://download.qt.io/online/qtsdkrepository/${url_os}/desktop/qt6_${qt_version_dotless}")
+ file(DOWNLOAD "${qt_base_url}/Updates.xml" ./Updates.xml SHOW_PROGRESS)
+
+ file(READ ./Updates.xml updates_xml)
+ string(REGEX MATCH "<Name>qt.qt6.*<Version>([0-9+-.]+)</Version>" updates_xml_output "${updates_xml}")
+ set(qt_package_version ${CMAKE_MATCH_1})
+
+ file(MAKE_DIRECTORY qt)
+
+ # Save the path for other steps
+ file(TO_CMAKE_PATH "$ENV{GITHUB_WORKSPACE}/qt/${qt_dir_prefix}" qt_dir)
+ message("::set-output name=qt_dir::${qt_dir}")
+
+ message("Downloading Qt to ${qt_dir}")
+ function(downloadAndExtract url archive)
+ message("Downloading ${url}")
+ file(DOWNLOAD "${url}" ./${archive} SHOW_PROGRESS)
+ execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ../${archive} WORKING_DIRECTORY qt)
+ endfunction()
+
+ foreach(package qtbase qtdeclarative)
+ downloadAndExtract(
+ "${qt_base_url}/qt.qt6.${qt_version_dotless}.${qt_package_arch_suffix}/${qt_package_version}${package}${qt_package_suffix}.7z"
+ ${package}.7z
+ )
+ endforeach()
+
+ foreach(package qt5compat qtshadertools)
+ downloadAndExtract(
+ "${qt_base_url}/qt.qt6.${qt_version_dotless}.${package}.${qt_package_arch_suffix}/${qt_package_version}${package}${qt_package_suffix}.7z"
+ ${package}.7z
+ )
+ endforeach()
+
+ # uic depends on libicu56.so
+ if ("${{ runner.os }}" STREQUAL "Linux")
+ downloadAndExtract(
+ "${qt_base_url}/qt.qt6.${qt_version_dotless}.${qt_package_arch_suffix}/${qt_package_version}icu-linux-Rhel7.2-x64.7z"
+ icu.7z
+ )
+ endif()
+
+ - name: Download Qt Creator
+ id: qt_creator
+ shell: cmake -P {0}
+ run: |
+ string(REGEX MATCH "([0-9]+.[0-9]+).[0-9]+" outvar "$ENV{QT_CREATOR_VERSION}")
+
+ set(qtc_base_url "https://download.qt.io/official_releases/qtcreator/${CMAKE_MATCH_1}/$ENV{QT_CREATOR_VERSION}/installer_source")
+ set(qtc_snapshot "$ENV{QT_CREATOR_SNAPSHOT}")
+ if (qtc_snapshot)
+ set(qtc_base_url "https://download.qt.io/snapshots/qtcreator/${CMAKE_MATCH_1}/$ENV{QT_CREATOR_VERSION}/installer_source/${qtc_snapshot}")
+ endif()
+
+ if ("${{ runner.os }}" STREQUAL "Windows")
+ set(qtc_platform "windows_x64")
+ elseif ("${{ runner.os }}" STREQUAL "Linux")
+ set(qtc_platform "linux_x64")
+ elseif ("${{ runner.os }}" STREQUAL "macOS")
+ set(qtc_platform "mac_x64")
+ endif()
+
+ file(TO_CMAKE_PATH "$ENV{GITHUB_WORKSPACE}/qtcreator" qtc_dir)
+ # Save the path for other steps
+ message("::set-output name=qtc_dir::${qtc_dir}")
+
+ file(MAKE_DIRECTORY qtcreator)
+
+ message("Downloading Qt Creator from ${qtc_base_url}/${qtc_platform}")
+
+ foreach(package qtcreator qtcreator_dev)
+ file(DOWNLOAD
+ "${qtc_base_url}/${qtc_platform}/${package}.7z" ./${package}.7z SHOW_PROGRESS)
+ execute_process(COMMAND
+ ${CMAKE_COMMAND} -E tar xvf ../${package}.7z WORKING_DIRECTORY qtcreator)
+ endforeach()
+
+ - name: Build
+ shell: cmake -P {0}
+ run: |
+ set(ENV{CC} ${{ matrix.config.cc }})
+ set(ENV{CXX} ${{ matrix.config.cxx }})
+ set(ENV{MACOSX_DEPLOYMENT_TARGET} "${{ env.MACOS_DEPLOYMENT_TARGET }}")
+
+ if ("${{ runner.os }}" STREQUAL "Windows" AND NOT "x${{ matrix.config.environment_script }}" STREQUAL "x")
+ execute_process(
+ COMMAND "${{ matrix.config.environment_script }}" && set
+ OUTPUT_FILE environment_script_output.txt
+ )
+ file(STRINGS environment_script_output.txt output_lines)
+ foreach(line IN LISTS output_lines)
+ if (line MATCHES "^([a-zA-Z0-9_-]+)=(.*)$")
+ set(ENV{${CMAKE_MATCH_1}} "${CMAKE_MATCH_2}")
+ endif()
+ endforeach()
+ endif()
+
+ set(ENV{NINJA_STATUS} "[%f/%t %o/sec] ")
+
+ set(build_plugin_py "scripts/build_plugin.py")
+ foreach(dir "share/qtcreator/scripts" "Qt Creator.app/Contents/Resources/scripts" "Contents/Resources/scripts")
+ if(EXISTS "${{ steps.qt_creator.outputs.qtc_dir }}/${dir}/build_plugin.py")
+ set(build_plugin_py "${dir}/build_plugin.py")
+ break()
+ endif()
+ endforeach()
+
+ if("${{ runner.os }}" STREQUAL "macOS")
+ set(architecture_config "--add-config=-DCMAKE_OSX_ARCHITECTURES=x86_64\;arm64")
+ endif()
+
+ execute_process(
+ COMMAND python
+ -u
+ ${{ steps.qt_creator.outputs.qtc_dir }}/${build_plugin_py}
+ --name "$ENV{PLUGIN_NAME}-$ENV{QT_CREATOR_VERSION}-${{ matrix.config.artifact }}"
+ --src .
+ --build build
+ --qt-path "${{ steps.qt.outputs.qt_dir }}"
+ --qtc-path "${{ steps.qt_creator.outputs.qtc_dir }}"
+ --output-path "$ENV{GITHUB_WORKSPACE}"
+ ${architecture_config}
+ RESULT_VARIABLE result
+ )
+ if (NOT result EQUAL 0)
+ string(REGEX MATCH "FAILED:.*$" error_message "${output}")
+ string(REPLACE "\n" "%0A" error_message "${error_message}")
+ message("::error::${error_message}")
+ message(FATAL_ERROR "Build failed")
+ endif()
+
+ - uses: actions/upload-artifact@v2
+ id: upload_artifact
+ with:
+ path: ./${{ env.PLUGIN_NAME }}-${{ env.QT_CREATOR_VERSION }}-${{ matrix.config.artifact }}.7z
+ name: ${{ env.PLUGIN_NAME}}-${{ env.QT_CREATOR_VERSION }}-${{ matrix.config.artifact }}.7z
+
+ release:
+ if: contains(github.ref, 'tags/v')
+ runs-on: ubuntu-latest
+ needs: build
+
+ steps:
+ - name: Create Release
+ id: create_release
+ uses: actions/create-release@v1.0.0
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ tag_name: ${{ github.ref }}
+ release_name: Release ${{ github.ref }}
+ draft: false
+ prerelease: false
+
+ - name: Store Release url
+ run: |
+ echo "${{ steps.create_release.outputs.upload_url }}" > ./upload_url
+
+ - uses: actions/upload-artifact@v1
+ with:
+ path: ./upload_url
+ name: upload_url
+
+ publish:
+ if: contains(github.ref, 'tags/v')
+
+ name: ${{ matrix.config.name }}
+ runs-on: ${{ matrix.config.os }}
+ strategy:
+ matrix:
+ config:
+ - {
+ name: "Windows Latest x64", artifact: "Windows-x64.7z",
+ os: ubuntu-latest
+ }
+ - {
+ name: "Linux Latest x64", artifact: "Linux-x64.7z",
+ os: ubuntu-latest
+ }
+ - {
+ name: "macOS Latest x64", artifact: "macOS-x64.7z",
+ os: macos-latest
+ }
+ needs: release
+
+ steps:
+ - name: Download artifact
+ uses: actions/download-artifact@v1
+ with:
+ name: ${{ env.PLUGIN_NAME }}-${{ env.QT_CREATOR_VERSION }}-${{ matrix.config.artifact }}
+ path: ./
+
+ - name: Download URL
+ uses: actions/download-artifact@v1
+ with:
+ name: upload_url
+ path: ./
+ - id: set_upload_url
+ run: |
+ upload_url=`cat ./upload_url`
+ echo ::set-output name=upload_url::$upload_url
+
+ - name: Upload to Release
+ id: upload_to_release
+ uses: actions/upload-release-asset@v1.0.1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ steps.set_upload_url.outputs.upload_url }}
+ asset_path: ./${{ env.PLUGIN_NAME }}-${{ env.QT_CREATOR_VERSION }}-${{ matrix.config.artifact }}
+ asset_name: ${{ env.PLUGIN_NAME }}-${{ env.QT_CREATOR_VERSION }}-${{ matrix.config.artifact }}
+ asset_content_type: application/x-7z-compressed
diff --git a/.gitignore b/.gitignore
index 8ee1bf9..aa71568 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
.stack-work
+CMakeLists.txt.user*
+/build*/
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..2ab32c3
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,14 @@
+cmake_minimum_required(VERSION 3.10)
+
+project(Haskell)
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+set(CMAKE_AUTOUIC ON)
+set(CMAKE_CXX_STANDARD 17)
+
+find_package(QtCreator COMPONENTS Core REQUIRED)
+find_package(Qt5 COMPONENTS Widgets REQUIRED)
+
+add_subdirectory(plugins/haskell)
+add_subdirectory(tests/auto/tokenizer)
diff --git a/README.md b/README.md
index 4efde0c..84f0ca2 100644
--- a/README.md
+++ b/README.md
@@ -1,24 +1,42 @@
-### Haskell Support for Qt Creator
+# Haskell Support for Qt Creator
This Qt Creator plugin adds basic support for the Haskell programming language.
-#### Features
+## Features
-* Code highlighting
-* Editor tool tips with symbol information
-* Follow symbol
+* Syntax highlighting
* Basic .cabal project support
* Basic build configuration
* Basic run configuration
-#### Requirements
+Other editing features like code completion and navigation are provided via
+[haskell-ide-engine](https://github.com/haskell/haskell-ide-engine) and Qt Creator's
+Language Server Protocol client.
+
+## Requirements
+
+### Projects
The plugin currently only supports projects using [Haskell Stack](https://haskellstack.org).
-The project must already be set up, if something mysteriously does not work check the following:
+* The plugin looks for the `stack` executable in the default installation directory of the Haskell
+ Stack installers. If this is not correct for you, adapt the path in *Options* > *Haskell*.
+
+Linux: Note that Haskell Stack from the Ubuntu distribution and probably others is hopelessly
+outdated. Use the installers provided by the [Haskell Stack](https://haskellstack.org) project.
+
+### Editing
+
+Install [haskell-ide-engine](https://github.com/haskell/haskell-ide-engine) for the GHC version
+that your project uses and [configure it](https://doc.qt.io/qtcreator/creator-language-servers.html)
+in Qt Creator's language client:
-* The project's resolver must be installed. Ensure this by running `stack setup` in the project directory.
-* For code info and navigation to work, `ghc-mod` is required to be built for the project's resolver. Ensure this by running `stack build ghc-mod` in the project directory.
-* The plugin looks for the `stack` executable in the default installation directory of the Haskell Stack installers. If this is not correct for you, adapt the path in *Options* > *Haskell*.
+* Open *Options* > *Language Client*
+* Add a new server
+* Set *Language* to the MIME types `text/x-haskell`, `text/x-haskell-project` and
+ `text/x-literate-haskell`
+* Set *Startup behavior* to *Start Server per Project*
+* Set *Executable* to `hie-wrapper`, for example to `/home/myself/.local/bin/hie-wrapper`
+* Set *Arguments* to `-lsp`
-Linux: Note that Haskell Stack from the Ubuntu distribution and probably others is hopelessly outdated. Use the installers provided by the [Haskell Stack](https://haskellstack.org) project. \ No newline at end of file
+Note that HIE compiles your project before providing any information, so it might take some time.
diff --git a/haskell.pro b/haskell.pro
deleted file mode 100644
index a8601b4..0000000
--- a/haskell.pro
+++ /dev/null
@@ -1,7 +0,0 @@
-TEMPLATE = subdirs
-
-SUBDIRS += \
- plugins/haskell
-
-!isEmpty(BUILD_TESTS): SUBDIRS += tests/auto
-
diff --git a/plugins/haskell/CMakeLists.txt b/plugins/haskell/CMakeLists.txt
new file mode 100644
index 0000000..8f6cc4f
--- /dev/null
+++ b/plugins/haskell/CMakeLists.txt
@@ -0,0 +1,27 @@
+add_qtc_plugin(Haskell
+ PLUGIN_DEPENDS
+ QtCreator::Core QtCreator::TextEditor QtCreator::ProjectExplorer
+ DEPENDS Qt5::Widgets
+ SOURCES
+ haskell.qrc
+ haskell_global.h
+ haskellbuildconfiguration.cpp haskellbuildconfiguration.h
+ haskellconstants.h
+ haskelleditorfactory.cpp haskelleditorfactory.h
+ haskellhighlighter.cpp haskellhighlighter.h
+ haskellmanager.cpp haskellmanager.h
+ haskellplugin.cpp haskellplugin.h
+ haskellproject.cpp haskellproject.h
+ haskellrunconfiguration.cpp haskellrunconfiguration.h
+ haskelltokenizer.cpp haskelltokenizer.h
+ optionspage.cpp optionspage.h
+ stackbuildstep.cpp stackbuildstep.h
+)
+
+qtc_add_resources(Haskell haskell_wizards
+ PREFIX "/haskell"
+ BASE share/wizards
+ FILES
+ module/file.hs
+ module/wizard.json
+)
diff --git a/plugins/haskell/config.pri b/plugins/haskell/config.pri
deleted file mode 100644
index 0e52377..0000000
--- a/plugins/haskell/config.pri
+++ /dev/null
@@ -1,12 +0,0 @@
-# Qt Creator linking
-
-## Either set the IDE_SOURCE_TREE when running qmake,
-## or set the QTC_SOURCE environment variable, to override the default setting
-isEmpty(IDE_SOURCE_TREE): IDE_SOURCE_TREE = $$(QTC_SOURCE)
-isEmpty(IDE_SOURCE_TREE): IDE_SOURCE_TREE = "$$PWD/../../../qtcreator_src"
-
-## Either set the IDE_BUILD_TREE when running qmake,
-## or set the QTC_BUILD environment variable, to override the default setting
-isEmpty(IDE_BUILD_TREE): IDE_BUILD_TREE = $$(QTC_BUILD)
-isEmpty(IDE_BUILD_TREE): IDE_BUILD_TREE = "$$PWD/../../../qtcreator_build"
-
diff --git a/plugins/haskell/filecache.cpp b/plugins/haskell/filecache.cpp
deleted file mode 100644
index 5d931c9..0000000
--- a/plugins/haskell/filecache.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-****************************************************************************/
-
-#include "filecache.h"
-
-#include <coreplugin/idocument.h>
-#include <texteditor/textdocument.h>
-#include <utils/algorithm.h>
-
-#include <QFile>
-#include <QLoggingCategory>
-#include <QTemporaryFile>
-#include <QTextDocument>
-
-Q_LOGGING_CATEGORY(cacheLog, "qtc.haskell.filecache", QtWarningMsg)
-
-using namespace Core;
-using namespace TextEditor;
-using namespace Utils;
-
-namespace Haskell {
-namespace Internal {
-
-FileCache::FileCache(const QString &id,
- const std::function<QList<Core::IDocument *>()> &documentsToUpdate)
- : m_tempDir(id),
- m_documentsToUpdate(documentsToUpdate)
-{
- qCDebug(cacheLog) << "creating cache path at" << m_tempDir.path();
-}
-
-
-void FileCache::update()
-{
- const QList<IDocument *> documents = m_documentsToUpdate();
- for (IDocument *document : documents) {
- const Utils::FilePath filePath = document->filePath();
- if (m_fileMap.contains(filePath)) {
- // update the existing cached file
- // check revision if possible
- if (auto textDocument = qobject_cast<TextDocument *>(document)) {
- if (m_fileRevision.value(filePath) != textDocument->document()->revision())
- writeFile(document);
- } else {
- writeFile(document);
- }
- } else {
- // save file if it is modified
- if (document->isModified())
- writeFile(document);
- }
- }
- cleanUp(documents);
-}
-
-QHash<FilePath, FilePath> FileCache::fileMap() const
-{
- return m_fileMap;
-}
-
-void FileCache::writeFile(IDocument *document)
-{
- FilePath cacheFilePath = m_fileMap.value(document->filePath());
- if (cacheFilePath.isEmpty()) {
- cacheFilePath = createCacheFile(document->filePath());
- m_fileMap.insert(document->filePath(), cacheFilePath);
- }
- qCDebug(cacheLog) << "writing" << cacheFilePath;
- if (auto baseTextDocument = qobject_cast<BaseTextDocument *>(document)) {
- QString errorMessage;
- if (baseTextDocument->write(cacheFilePath.toString(),
- QString::fromUtf8(baseTextDocument->contents()),
- &errorMessage)) {
- if (auto textDocument = qobject_cast<TextDocument *>(document)) {
- m_fileRevision.insert(document->filePath(), textDocument->document()->revision());
- } else {
- m_fileRevision.insert(document->filePath(), -1);
- }
- } else {
- qCDebug(cacheLog) << "!!! writing file failed:" << errorMessage;
- }
-
- } else {
- QFile file(cacheFilePath.toString());
- if (file.open(QIODevice::WriteOnly)) {
- file.write(document->contents());
- } else {
- qCDebug(cacheLog) << "!!! opening file for writing failed";
- }
- }
-}
-
-void FileCache::cleanUp(const QList<IDocument *> &documents)
-{
- const QSet<FilePath> files = Utils::transform<QSet>(documents, &IDocument::filePath);
- auto it = QMutableHashIterator<FilePath, FilePath>(m_fileMap);
- while (it.hasNext()) {
- it.next();
- if (!files.contains(it.key())) {
- qCDebug(cacheLog) << "deleting" << it.value();
- QFile::remove(it.value().toString());
- m_fileRevision.remove(it.key());
- it.remove();
- }
- }
-}
-
-FilePath FileCache::createCacheFile(const FilePath &filePath)
-{
- QTemporaryFile tempFile(m_tempDir.path() + "/XXXXXX-" + filePath.fileName());
- tempFile.setAutoRemove(false);
- tempFile.open();
- return FilePath::fromString(tempFile.fileName());
-}
-
-} // namespace Internal
-} // namespace Haskell
diff --git a/plugins/haskell/filecache.h b/plugins/haskell/filecache.h
deleted file mode 100644
index 3c53d40..0000000
--- a/plugins/haskell/filecache.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-****************************************************************************/
-
-#pragma once
-
-#include <utils/fileutils.h>
-#include <utils/temporarydirectory.h>
-
-#include <QHash>
-
-#include <functional>
-
-namespace Core { class IDocument; }
-
-namespace Haskell {
-namespace Internal {
-
-class FileCache
-{
-public:
- FileCache(const QString &id,
- const std::function<QList<Core::IDocument *>()> &documentsToUpdate);
-
- void update();
- QHash<Utils::FilePath, Utils::FilePath> fileMap() const;
-
-private:
- void writeFile(Core::IDocument *document);
- void cleanUp(const QList<Core::IDocument *> &documents);
- Utils::FilePath createCacheFile(const Utils::FilePath &filePath);
-
- Utils::TemporaryDirectory m_tempDir;
- QHash<Utils::FilePath, Utils::FilePath> m_fileMap;
- QHash<Utils::FilePath, int> m_fileRevision;
- std::function<QList<Core::IDocument *>()> m_documentsToUpdate;
-};
-
-} // namespace Internal
-} // namespace Haskell
diff --git a/plugins/haskell/followsymbol.cpp b/plugins/haskell/followsymbol.cpp
deleted file mode 100644
index ab32614..0000000
--- a/plugins/haskell/followsymbol.cpp
+++ /dev/null
@@ -1,141 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-****************************************************************************/
-
-#include "followsymbol.h"
-
-#include "haskelleditorwidget.h"
-#include "haskellmanager.h"
-#include "haskelltokenizer.h"
-
-#include <coreplugin/editormanager/editormanager.h>
-#include <texteditor/codeassist/assistinterface.h>
-#include <texteditor/codeassist/assistproposalitem.h>
-#include <texteditor/codeassist/genericproposal.h>
-#include <texteditor/codeassist/genericproposalmodel.h>
-
-#include <utils/qtcassert.h>
-
-using namespace TextEditor;
-using namespace Utils;
-
-namespace Haskell {
-namespace Internal {
-
-IAssistProvider::RunType FollowSymbolAssistProvider::runType() const
-{
- return AsynchronousWithThread;
-}
-
-IAssistProcessor *FollowSymbolAssistProvider::createProcessor() const
-{
- return new FollowSymbolAssistProcessor(m_inNextSplit);
-}
-
-void FollowSymbolAssistProvider::setOpenInNextSplit(bool inNextSplit)
-{
- m_inNextSplit = inNextSplit;
-}
-
-FollowSymbolAssistProcessor::FollowSymbolAssistProcessor(bool inNextSplit)
- : m_inNextSplit(inNextSplit)
-{
-}
-
-IAssistProposal *FollowSymbolAssistProcessor::immediateProposal(const AssistInterface *interface)
-{
- int line, column;
- const optional<Token> symbol
- = HaskellEditorWidget::symbolAt(interface->textDocument(), interface->position(),
- &line, &column);
- QTC_ASSERT(symbol, return nullptr); // should have been checked before
- const auto filePath = FilePath::fromString(interface->fileName());
- m_ghcmod = HaskellManager::ghcModForFile(filePath);
- m_symbolFuture = m_ghcmod->findSymbol(filePath, symbol->text.toString());
-
- auto item = new AssistProposalItem();
- item->setText(HaskellManager::trLookingUp(symbol->text.toString()));
- item->setData(QString());
- item->setOrder(-1000);
-
- const QList<TextEditor::AssistProposalItemInterface *> list = {item};
- auto proposal = new GenericProposal(interface->position(), list);
- proposal->setFragile(true);
- return proposal;
-}
-
-IAssistProposal *FollowSymbolAssistProcessor::perform(const AssistInterface *interface)
-{
- const int position = interface->position();
- delete interface;
- const SymbolInfoOrError info = m_symbolFuture.result();
- auto item = new FollowSymbolAssistProposalItem(m_ghcmod->basePath(), info, m_inNextSplit);
- return new InstantProposal(position, {item});
-}
-
-FollowSymbolAssistProposalItem::FollowSymbolAssistProposalItem(const FilePath &basePath,
- const SymbolInfoOrError &info,
- bool inNextSplit)
- : m_basePath(basePath),
- m_inNextSplit(inNextSplit)
-{
- const SymbolInfo *info_p = Utils::get_if<SymbolInfo>(&info);
- if (info_p && !info_p->file.isEmpty()) {
- m_info = info;
- setText(m_basePath.toString() + '/' + info_p->file.toString());
- }
-}
-
-void FollowSymbolAssistProposalItem::apply(TextDocumentManipulatorInterface &, int) const
-{
- const SymbolInfo *info_p = Utils::get_if<SymbolInfo>(&m_info);
- if (info_p)
- Core::EditorManager::openEditorAt(m_basePath.toString() + '/' + info_p->file.toString(),
- info_p->line, info_p->col - 1, Core::Id(),
- m_inNextSplit ? Core::EditorManager::OpenInOtherSplit
- : Core::EditorManager::NoFlags);
-}
-
-void InstantActivationProposalWidget::showProposal(const QString &prefix)
-{
- if (model() && model()->size() == 1) {
- emit proposalItemActivated(model()->proposalItem(0));
- deleteLater();
- return;
- }
- GenericProposalWidget::showProposal(prefix);
-}
-
-InstantProposal::InstantProposal(int cursorPos, const QList<AssistProposalItemInterface *> &items)
- : GenericProposal(cursorPos, items)
-{
-}
-
-IAssistProposalWidget *InstantProposal::createWidget() const
-{
- return new InstantActivationProposalWidget();
-}
-
-} // namespace Internal
-} // namespace Haskell
diff --git a/plugins/haskell/followsymbol.h b/plugins/haskell/followsymbol.h
deleted file mode 100644
index a4f9051..0000000
--- a/plugins/haskell/followsymbol.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-****************************************************************************/
-
-#pragma once
-
-#include "ghcmod.h"
-
-#include <texteditor/codeassist/assistproposalitem.h>
-#include <texteditor/codeassist/genericproposal.h>
-#include <texteditor/codeassist/genericproposalwidget.h>
-#include <texteditor/codeassist/iassistprocessor.h>
-#include <texteditor/codeassist/iassistprovider.h>
-
-namespace Haskell {
-namespace Internal {
-
-class FollowSymbolAssistProposalItem : public TextEditor::AssistProposalItem
-{
-public:
- FollowSymbolAssistProposalItem(const Utils::FilePath &basePath,
- const SymbolInfoOrError &info,
- bool inNextSplit);
-
- void apply(TextEditor::TextDocumentManipulatorInterface &, int) const override;
-
-private:
- Utils::FilePath m_basePath;
- SymbolInfoOrError m_info;
- bool m_inNextSplit;
-};
-
-class InstantActivationProposalWidget : public TextEditor::GenericProposalWidget
-{
-protected:
- void showProposal(const QString &prefix) override;
-};
-
-class InstantProposal : public TextEditor::GenericProposal
-{
-public:
- InstantProposal(int cursorPos, const QList<TextEditor::AssistProposalItemInterface *> &items);
-
- TextEditor::IAssistProposalWidget *createWidget() const override;
-};
-
-class FollowSymbolAssistProvider : public TextEditor::IAssistProvider
-{
-public:
- RunType runType() const override;
- TextEditor::IAssistProcessor *createProcessor() const override;
- void setOpenInNextSplit(bool inNextSplit);
-
-private:
- bool m_inNextSplit = false;
-};
-
-class FollowSymbolAssistProcessor : public TextEditor::IAssistProcessor
-{
-public:
- FollowSymbolAssistProcessor(bool inNextSplit);
-
- TextEditor::IAssistProposal *immediateProposal(const TextEditor::AssistInterface *interface) override;
- TextEditor::IAssistProposal *perform(const TextEditor::AssistInterface *interface) override;
-
-private:
- std::shared_ptr<AsyncGhcMod> m_ghcmod;
- QFuture<SymbolInfoOrError> m_symbolFuture;
- bool m_inNextSplit;
-};
-
-} // namespace Internal
-} // namespace Haskell
diff --git a/plugins/haskell/ghcmod.cpp b/plugins/haskell/ghcmod.cpp
deleted file mode 100644
index 1ca7713..0000000
--- a/plugins/haskell/ghcmod.cpp
+++ /dev/null
@@ -1,408 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-****************************************************************************/
-
-#include "ghcmod.h"
-
-#include <coreplugin/editormanager/documentmodel.h>
-#include <coreplugin/idocument.h>
-#include <utils/algorithm.h>
-#include <utils/environment.h>
-#include <utils/qtcassert.h>
-
-#include <QFileInfo>
-#include <QFutureWatcher>
-#include <QLoggingCategory>
-#include <QMetaObject>
-#include <QMutexLocker>
-#include <QProcess>
-#include <QRegularExpression>
-#include <QTime>
-#include <QTimer>
-
-#include <functional>
-
-Q_LOGGING_CATEGORY(ghcModLog, "qtc.haskell.ghcmod", QtWarningMsg)
-Q_LOGGING_CATEGORY(asyncGhcModLog, "qtc.haskell.ghcmod.async", QtWarningMsg)
-
-// TODO do not hardcode
-const int kTimeoutMS = 10 * 1000;
-
-using namespace Utils;
-
-namespace Haskell {
-namespace Internal {
-
-FilePath GhcMod::m_stackExecutable = Utils::FilePath::fromString("stack");
-QMutex GhcMod::m_mutex;
-
-GhcMod::GhcMod(const Utils::FilePath &path)
- : m_path(path)
-{
-}
-
-GhcMod::~GhcMod()
-{
- shutdown();
-}
-
-FilePath GhcMod::basePath() const
-{
- return m_path;
-}
-
-void GhcMod::setFileMap(const QHash<FilePath, FilePath> &fileMap)
-{
- if (fileMap != m_fileMap) {
- log("setting new file map");
- m_fileMap = fileMap;
- shutdown();
- }
-}
-
-static QString toUnicode(QByteArray data)
-{
- // clean zero bytes which let QString think that the string ends there
- data.replace('\x0', QByteArray());
- return QString::fromUtf8(data);
-}
-
-SymbolInfoOrError GhcMod::findSymbol(const FilePath &filePath, const QString &symbol)
-{
- return parseFindSymbol(runFindSymbol(filePath, symbol));
-}
-
-QStringOrError GhcMod::typeInfo(const FilePath &filePath, int line, int col)
-{
- return parseTypeInfo(runTypeInfo(filePath, line, col));
-}
-
-static QStringList fileMapArgs(const QHash<FilePath, FilePath> &map)
-{
- QStringList result;
- const auto end = map.cend();
- for (auto it = map.cbegin(); it != end; ++it)
- result << "--map-file" << it.key().toString() + "=" + it.value().toString();
- return result;
-}
-
-Utils::optional<Error> GhcMod::ensureStarted()
-{
- m_mutex.lock();
- const FilePath plainStackExecutable = m_stackExecutable;
- m_mutex.unlock();
- Environment env = Environment::systemEnvironment();
- const FilePath stackExecutable = env.searchInPath(plainStackExecutable.toString());
- if (m_process) {
- if (m_process->state() == QProcess::NotRunning) {
- log("is no longer running");
- m_process.reset();
- } else if (FilePath::fromString(m_process->program()) != stackExecutable) {
- log("stack settings changed");
- shutdown();
- }
- }
- if (m_process)
- return Utils::nullopt;
- log("starting");
- // for ghc-mod finding stack back:
- env.prependOrSetPath(stackExecutable.toFileInfo().absolutePath());
- m_process.reset(new QProcess);
- m_process->setWorkingDirectory(m_path.toString());
- m_process->setEnvironment(env.toStringList());
- m_process->start(stackExecutable.toString(),
- QStringList{"exec", "ghc-mod", "--"} + fileMapArgs(m_fileMap)
- << "legacy-interactive");
- if (!m_process->waitForStarted(kTimeoutMS)) {
- log("failed to start");
- m_process.reset();
- return Error({Error::Type::FailedToStartStack, plainStackExecutable.toUserOutput()});
- }
- log("started");
- m_process->setReadChannel(QProcess::StandardOutput);
- return Utils::nullopt;
-}
-
-void GhcMod::shutdown()
-{
- if (!m_process)
- return;
- log("shutting down");
- m_process->write("\n");
- m_process->closeWriteChannel();
- m_process->waitForFinished(300);
- m_process.reset();
-}
-
-void GhcMod::log(const QString &message)
-{
- qCDebug(ghcModLog) << "ghcmod for" << m_path.toString() << ":" << qPrintable(message);
-}
-
-QByteArrayOrError GhcMod::runQuery(const QString &query)
-{
- const Utils::optional<Error> error = ensureStarted();
- if (error)
- return error.value();
- log("query \"" + query + "\"");
- m_process->write(query.toUtf8() + "\n");
- bool ok = false;
- QByteArray response;
- QTime readTime;
- readTime.start();
- while (!ok && readTime.elapsed() < kTimeoutMS) {
- m_process->waitForReadyRead(kTimeoutMS - readTime.elapsed() + 10);
- response += m_process->read(2048);
- ok = response.endsWith("OK\n") || response.endsWith("OK\r\n");
- }
- if (ghcModLog().isDebugEnabled())
- qCDebug(ghcModLog) << "response" << QTextCodec::codecForLocale()->toUnicode(response);
- if (!ok) {
- log("failed");
- shutdown();
- return Error({Error::Type::Other, QString()});
- }
- log("success");
- // convert to unix line endings
- response.replace("\r\n", "\n");
- response.chop(3); // cut off "OK\n"
- return response;
-}
-
-QByteArrayOrError GhcMod::runFindSymbol(const FilePath &filePath, const QString &symbol)
-{
- return runQuery(QString("info %1 %2").arg(filePath.toString()) // TODO toNative? quoting?
- .arg(symbol));
-}
-
-QByteArrayOrError GhcMod::runTypeInfo(const FilePath &filePath, int line, int col)
-{
- return runQuery(QString("type %1 %2 %3").arg(filePath.toString()) // TODO toNative? quoting?
- .arg(line)
- .arg(col + 1));
-}
-
-SymbolInfoOrError GhcMod::parseFindSymbol(const QByteArrayOrError &response)
-{
- QRegularExpression infoRegEx("^\\s*(.*?)\\s+--\\sDefined ((at (.+?)(:(\\d+):(\\d+))?)|(in ‘(.+)’.*))$");
- const QByteArray *bytes = Utils::get_if<QByteArray>(&response);
- if (!bytes)
- return Utils::get<Error>(response);
- SymbolInfo info;
- bool hasFileOrModule = false;
- const QString str = toUnicode(QByteArray(*bytes).replace('\x0', '\n'));
- for (const QString &line : str.split('\n')) {
- if (hasFileOrModule) {
- info.additionalInfo += line;
- } else {
- QRegularExpressionMatch result = infoRegEx.match(line);
- if (result.hasMatch()) {
- hasFileOrModule = true;
- info.definition += result.captured(1);
- if (result.lastCapturedIndex() == 7) { // Defined at <file:line:col>
- info.file = FilePath::fromString(result.captured(4));
- bool ok;
- int num = result.captured(6).toInt(&ok);
- if (ok)
- info.line = num;
- num = result.captured(7).toInt(&ok);
- if (ok)
- info.col = num;
- } else if (result.lastCapturedIndex() == 9) { // Defined in <module>
- info.module = result.captured(9);
- }
- } else {
- info.definition += line;
- }
- }
- }
- if (hasFileOrModule)
- return info;
- return Error({Error::Type::Other, QString()});
-}
-
-QStringOrError GhcMod::parseTypeInfo(const QByteArrayOrError &response)
-{
- QRegularExpression typeRegEx("^\\d+\\s+\\d+\\s+\\d+\\s+\\d+\\s+\"(.*)\"$",
- QRegularExpression::MultilineOption);
- const QByteArray *bytes = Utils::get_if<QByteArray>(&response);
- if (!bytes)
- return Utils::get<Error>(response);
- QRegularExpressionMatch result = typeRegEx.match(toUnicode(*bytes));
- if (result.hasMatch())
- return result.captured(1);
- return Error({Error::Type::Other, QString()});
-}
-
-void GhcMod::setStackExecutable(const FilePath &filePath)
-{
- QMutexLocker lock(&m_mutex);
- m_stackExecutable = filePath;
-}
-
-static QList<Core::IDocument *> getOpenDocuments(const FilePath &path)
-{
- return Utils::filtered(Core::DocumentModel::openedDocuments(), [path] (Core::IDocument *doc) {
- return path.isEmpty() || doc->filePath().isChildOf(path);
- });
-}
-
-AsyncGhcMod::AsyncGhcMod(const FilePath &path)
- : m_ghcmod(path),
- m_fileCache("haskell", std::bind(getOpenDocuments, path))
-{
- qCDebug(asyncGhcModLog) << "starting thread for" << m_ghcmod.basePath().toString();
- m_thread.start();
- m_threadTarget.moveToThread(&m_thread);
-}
-
-AsyncGhcMod::~AsyncGhcMod()
-{
- qCDebug(asyncGhcModLog) << "stopping thread for" << m_ghcmod.basePath().toString();
- m_mutex.lock();
- for (Operation &op : m_queue)
- op.fi.cancel();
- m_queue.clear();
- m_mutex.unlock();
- m_thread.quit();
- m_thread.wait();
-}
-
-FilePath AsyncGhcMod::basePath() const
-{
- return m_ghcmod.basePath();
-}
-
-template <typename Result>
-QFuture<Result> createFuture(AsyncGhcMod::Operation op,
- const std::function<Result(const QByteArrayOrError &)> &postOp)
-{
- auto fi = new QFutureInterface<Result>;
- fi->reportStarted();
-
- // propagate inner events to outside future
- auto opWatcher = new QFutureWatcher<QByteArrayOrError>();
- QObject::connect(opWatcher, &QFutureWatcherBase::canceled, [fi] { fi->cancel(); });
- QObject::connect(opWatcher, &QFutureWatcherBase::finished, opWatcher, &QObject::deleteLater);
- QObject::connect(opWatcher, &QFutureWatcherBase::finished, [fi] {
- fi->reportFinished();
- delete fi;
- });
- QObject::connect(opWatcher, &QFutureWatcherBase::resultReadyAt,
- [fi, opWatcher, postOp](int index) {
- fi->reportResult(postOp(opWatcher->future().resultAt(index)));
- });
- opWatcher->setFuture(op.fi.future());
-
- // propagate cancel from outer future to inner future
- auto fiWatcher = new QFutureWatcher<Result>();
- QObject::connect(fiWatcher, &QFutureWatcherBase::canceled, [op] { op.fi.cancel(); });
- QObject::connect(fiWatcher, &QFutureWatcherBase::finished, fiWatcher, &QObject::deleteLater);
- fiWatcher->setFuture(fi->future());
-
- return fi->future();
-}
-
-/*!
- Asynchronously looks up the \a symbol in the context of \a filePath.
-
- Returns a QFuture handle for the asynchronous operation. You may not block the main event loop
- while waiting for it to finish - doing so will result in a deadlock.
-*/
-QFuture<SymbolInfoOrError> AsyncGhcMod::findSymbol(const FilePath &filePath,
- const QString &symbol)
-{
- QMutexLocker lock(&m_mutex);
- Operation op([this, filePath, symbol] { return m_ghcmod.runFindSymbol(filePath, symbol); });
- m_queue.append(op);
- QTimer::singleShot(0, &m_threadTarget, [this] { reduceQueue(); });
- return createFuture<SymbolInfoOrError>(op, &GhcMod::parseFindSymbol);
-}
-
-/*!
- Asynchronously looks up the type at \a line and \a col in \a filePath.
-
- Returns a QFuture handle for the asynchronous operation. You may not block the main event loop
- while waiting for it to finish - doing so will result in a deadlock.
-*/
-QFuture<QStringOrError> AsyncGhcMod::typeInfo(const FilePath &filePath, int line, int col)
-{
- QMutexLocker lock(&m_mutex);
- Operation op([this, filePath, line, col] { return m_ghcmod.runTypeInfo(filePath, line, col); });
- m_queue.append(op);
- QTimer::singleShot(0, &m_threadTarget, [this] { reduceQueue(); });
- return createFuture<QStringOrError>(op, &GhcMod::parseTypeInfo);
-}
-
-/*!
- Synchronously runs an update of the cache of modified files.
- This must be run on the main thread.
-
- \internal
-*/
-void AsyncGhcMod::updateCache()
-{
- m_fileCache.update();
-}
-
-/*!
- Takes operations from the queue and executes them, until the queue is empty.
- This must be run within the internal thread whenever an operation is added to the queue.
- Canceled operations are not executed, but removed from the queue.
- Before each operation, the cache of modified files is updated on the main thread.
-
- \internal
-*/
-void AsyncGhcMod::reduceQueue()
-{
- QTC_ASSERT(QThread::currentThread() != thread(), return);
- const auto takeFirst = [this]() {
- Operation op;
- m_mutex.lock();
- if (!m_queue.isEmpty())
- op = m_queue.takeFirst();
- m_mutex.unlock();
- return op;
- };
-
- Operation op;
- while ((op = takeFirst()).op) {
- if (!op.fi.isCanceled()) {
- QMetaObject::invokeMethod(this, "updateCache", Qt::BlockingQueuedConnection);
- m_ghcmod.setFileMap(m_fileCache.fileMap());
- QByteArrayOrError result = op.op();
- op.fi.reportResult(result);
- }
- op.fi.reportFinished();
- }
-}
-
-AsyncGhcMod::Operation::Operation(const std::function<QByteArrayOrError()> &op)
- : op(op)
-{
- fi.reportStarted();
-}
-
-} // Internal
-} // Haskell
diff --git a/plugins/haskell/ghcmod.h b/plugins/haskell/ghcmod.h
deleted file mode 100644
index fb3dd3f..0000000
--- a/plugins/haskell/ghcmod.h
+++ /dev/null
@@ -1,150 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-****************************************************************************/
-
-#pragma once
-
-#include "filecache.h"
-
-#include <utils/fileutils.h>
-#include <utils/optional.h>
-#include <utils/synchronousprocess.h>
-#include <utils/variant.h>
-
-#include <QFuture>
-#include <QMutex>
-#include <QThread>
-
-#include <memory>
-
-QT_BEGIN_NAMESPACE
-class QProcess;
-QT_END_NAMESPACE
-
-namespace Haskell {
-namespace Internal {
-
-class Error {
-public:
- enum class Type {
- FailedToStartStack,
- Other // TODO get rid of it
- };
- Type type;
- QString details;
-};
-
-class SymbolInfo {
-public:
- QStringList definition;
- QStringList additionalInfo;
- Utils::FilePath file;
- int line = -1;
- int col = -1;
- QString module;
-};
-
-using QByteArrayOrError = Utils::variant<QByteArray, Error>;
-using QStringOrError = Utils::variant<QString, Error>;
-using SymbolInfoOrError = Utils::variant<SymbolInfo, Error>;
-
-template <typename T> class ghcmod_deleter;
-template <> class ghcmod_deleter<QProcess>
-{
-public:
- void operator()(QProcess *p) { Utils::SynchronousProcess::stopProcess(*p); delete p; }
-};
-using unique_ghcmod_process = std::unique_ptr<QProcess, ghcmod_deleter<QProcess>>;
-
-class GhcMod
-{
-public:
- GhcMod(const Utils::FilePath &path);
- ~GhcMod();
-
- Utils::FilePath basePath() const;
- void setFileMap(const QHash<Utils::FilePath, Utils::FilePath> &fileMap);
-
- SymbolInfoOrError findSymbol(const Utils::FilePath &filePath, const QString &symbol);
- QStringOrError typeInfo(const Utils::FilePath &filePath, int line, int col);
-
- QByteArrayOrError runQuery(const QString &query);
-
- QByteArrayOrError runFindSymbol(const Utils::FilePath &filePath, const QString &symbol);
- QByteArrayOrError runTypeInfo(const Utils::FilePath &filePath, int line, int col);
-
- static SymbolInfoOrError parseFindSymbol(const QByteArrayOrError &response);
- static QStringOrError parseTypeInfo(const QByteArrayOrError &response);
-
- static void setStackExecutable(const Utils::FilePath &filePath);
-
-private:
- Utils::optional<Error> ensureStarted();
- void shutdown();
- void log(const QString &message);
-
- static Utils::FilePath m_stackExecutable;
- static QMutex m_mutex;
-
- Utils::FilePath m_path;
- unique_ghcmod_process m_process; // kills process on reset
- QHash<Utils::FilePath, Utils::FilePath> m_fileMap;
-};
-
-class AsyncGhcMod : public QObject
-{
- Q_OBJECT
-
-public:
- struct Operation {
- Operation() = default;
- Operation(const std::function<QByteArrayOrError()> &op);
- mutable QFutureInterface<QByteArrayOrError> fi;
- std::function<QByteArrayOrError()> op;
- };
-
- AsyncGhcMod(const Utils::FilePath &path);
- ~AsyncGhcMod();
-
- Utils::FilePath basePath() const;
-
- QFuture<SymbolInfoOrError> findSymbol(const Utils::FilePath &filePath, const QString &symbol);
- QFuture<QStringOrError> typeInfo(const Utils::FilePath &filePath, int line, int col);
-
-private slots:
- void updateCache(); // called through QMetaObject::invokeMethod
-
-private:
- void reduceQueue();
-
- QThread m_thread;
- QObject m_threadTarget; // used to run methods in m_thread
- GhcMod m_ghcmod; // only use in m_thread
- FileCache m_fileCache; // only update through reduceQueue
- QVector<Operation> m_queue;
- QMutex m_mutex;
-};
-
-} // Internal
-} // Haskell
diff --git a/plugins/haskell/haskell.pro b/plugins/haskell/haskell.pro
deleted file mode 100644
index 92d0307..0000000
--- a/plugins/haskell/haskell.pro
+++ /dev/null
@@ -1,75 +0,0 @@
-DEFINES += HASKELL_LIBRARY
-
-# Haskell files
-
-SOURCES += \
- haskellcompletionassist.cpp \
- haskelleditorfactory.cpp \
- haskellhoverhandler.cpp \
- haskellplugin.cpp \
- haskellhighlighter.cpp \
- haskelltokenizer.cpp \
- ghcmod.cpp \
- haskellmanager.cpp \
- haskelldocument.cpp \
- optionspage.cpp \
- filecache.cpp \
- haskelleditorwidget.cpp \
- followsymbol.cpp \
- haskellproject.cpp \
- haskellbuildconfiguration.cpp \
- stackbuildstep.cpp \
- haskellrunconfiguration.cpp
-
-HEADERS += \
- haskell_global.h \
- haskellcompletionassist.h \
- haskellconstants.h \
- haskelleditorfactory.h \
- haskellhoverhandler.h \
- haskellplugin.h \
- haskellhighlighter.h \
- haskelltokenizer.h \
- ghcmod.h \
- haskellmanager.h \
- haskelldocument.h \
- optionspage.h \
- filecache.h \
- haskelleditorwidget.h \
- followsymbol.h \
- haskellproject.h \
- haskellbuildconfiguration.h \
- stackbuildstep.h \
- haskellrunconfiguration.h
-
-## uncomment to build plugin into user config directory
-## <localappdata>/plugins/<ideversion>
-## where <localappdata> is e.g.
-## "%LOCALAPPDATA%\QtProject\qtcreator" on Windows Vista and later
-## "$XDG_DATA_HOME/data/QtProject/qtcreator" or "~/.local/share/data/QtProject/qtcreator" on Linux
-## "~/Library/Application Support/QtProject/Qt Creator" on OS X
-#USE_USER_DESTDIR = yes
-
-###### If the plugin can be depended upon by other plugins, this code needs to be outsourced to
-###### <dirname>_dependencies.pri, where <dirname> is the name of the directory containing the
-###### plugin's sources.
-
-QTC_PLUGIN_NAME = Haskell
-QTC_LIB_DEPENDS += \
- # nothing here at this time
-
-QTC_PLUGIN_DEPENDS += \
- coreplugin \
- projectexplorer \
- texteditor
-
-QTC_PLUGIN_RECOMMENDS += \
- # optional plugin dependencies. nothing here at this time
-
-###### End _dependencies.pri contents ######
-
-include(config.pri)
-include($$IDE_SOURCE_TREE/src/qtcreatorplugin.pri)
-
-RESOURCES += \
- haskell.qrc
diff --git a/plugins/haskell/haskell.qbs b/plugins/haskell/haskell.qbs
index d2296a9..30eb832 100644
--- a/plugins/haskell/haskell.qbs
+++ b/plugins/haskell/haskell.qbs
@@ -11,19 +11,12 @@ QtcPlugin {
Depends { name: "ProjectExplorer" }
files: [
- "filecache.cpp", "filecache.h",
- "followsymbol.cpp", "followsymbol.h",
- "ghcmod.cpp", "ghcmod.h",
"haskell.qrc",
"haskellbuildconfiguration.cpp", "haskellbuildconfiguration.h",
- "haskellcompletionassist.cpp", "haskellcompletionassist.h",
"haskellconstants.h",
- "haskelldocument.cpp", "haskelldocument.h",
"haskelleditorfactory.cpp", "haskelleditorfactory.h",
- "haskelleditorwidget.cpp", "haskelleditorwidget.h",
"haskell_global.h",
"haskellhighlighter.cpp", "haskellhighlighter.h",
- "haskellhoverhandler.cpp", "haskellhoverhandler.h",
"haskellmanager.cpp", "haskellmanager.h",
"haskellplugin.cpp", "haskellplugin.h",
"haskellproject.cpp", "haskellproject.h",
diff --git a/plugins/haskell/haskellbuildconfiguration.cpp b/plugins/haskell/haskellbuildconfiguration.cpp
index 0233e15..c133d2d 100644
--- a/plugins/haskell/haskellbuildconfiguration.cpp
+++ b/plugins/haskell/haskellbuildconfiguration.cpp
@@ -27,7 +27,6 @@
#include "haskellconstants.h"
#include "haskellproject.h"
-#include "stackbuildstep.h"
#include <projectexplorer/buildinfo.h>
#include <projectexplorer/buildsteplist.h>
@@ -36,7 +35,7 @@
#include <projectexplorer/target.h>
#include <utils/algorithm.h>
#include <utils/detailswidget.h>
-#include <utils/mimetypes/mimedatabase.h>
+#include <utils/mimeutils.h>
#include <utils/pathchooser.h>
#include <utils/qtcassert.h>
@@ -56,41 +55,31 @@ HaskellBuildConfigurationFactory::HaskellBuildConfigurationFactory()
registerBuildConfiguration<HaskellBuildConfiguration>(C_HASKELL_BUILDCONFIGURATION_ID);
setSupportedProjectType(Constants::C_HASKELL_PROJECT_ID);
setSupportedProjectMimeTypeName(Constants::C_HASKELL_PROJECT_MIMETYPE);
-}
-
-static QList<BuildInfo> createInfos(const HaskellBuildConfigurationFactory *factory,
- const Kit *k,
- const Utils::FilePath &projectFilePath)
-{
- BuildInfo info(factory);
- info.typeName = HaskellBuildConfigurationFactory::tr("Release");
- info.displayName = info.typeName;
- info.buildDirectory = projectFilePath.parentDir().pathAppended(".stack-work");
- info.kitId = k->id();
- info.buildType = BuildConfiguration::BuildType::Release;
- return {info};
-}
-QList<BuildInfo> HaskellBuildConfigurationFactory::availableBuilds(const Target *parent) const
-{
- // Entries that are available in add build configuration dropdown
- return Utils::transform(createInfos(this, parent->kit(), parent->project()->projectFilePath()),
- [](BuildInfo info) {
- info.displayName.clear();
- return info;
- });
+ setBuildGenerator([](const Kit *k, const Utils::FilePath &projectPath, bool forSetup) {
+ BuildInfo info;
+ info.typeName = HaskellBuildConfiguration::tr("Release");
+ if (forSetup) {
+ info.displayName = info.typeName;
+ info.buildDirectory = projectPath.parentDir().pathAppended(".stack-work");
+ }
+ info.kitId = k->id();
+ info.buildType = BuildConfiguration::BuildType::Release;
+ return QList<BuildInfo>{info};
+ });
}
-QList<BuildInfo> HaskellBuildConfigurationFactory::availableSetups(
- const Kit *k, const QString &projectPath) const
+HaskellBuildConfiguration::HaskellBuildConfiguration(Target *target, Utils::Id id)
+ : BuildConfiguration(target, id)
{
- return createInfos(this, k, Utils::FilePath::fromString(projectPath));
+ setInitializer([this](const BuildInfo &info) {
+ setBuildDirectory(info.buildDirectory);
+ setBuildType(info.buildType);
+ setDisplayName(info.displayName);
+ });
+ appendInitialBuildStep(Constants::C_STACK_BUILD_STEP_ID);
}
-HaskellBuildConfiguration::HaskellBuildConfiguration(Target *target, Core::Id id)
- : BuildConfiguration(target, id)
-{}
-
NamedWidget *HaskellBuildConfiguration::createConfigWidget()
{
return new HaskellBuildConfigurationWidget(this);
@@ -106,50 +95,37 @@ void HaskellBuildConfiguration::setBuildType(BuildConfiguration::BuildType type)
m_buildType = type;
}
-void HaskellBuildConfiguration::initialize(const BuildInfo &info)
-{
- BuildConfiguration::initialize(info);
- setBuildDirectory(info.buildDirectory);
- setBuildType(info.buildType);
- setDisplayName(info.displayName);
-
- BuildStepList *buildSteps = stepList(ProjectExplorer::Constants::BUILDSTEPS_BUILD);
- auto stackBuildStep = new StackBuildStep(buildSteps);
- buildSteps->appendStep(stackBuildStep);
-}
-
HaskellBuildConfigurationWidget::HaskellBuildConfigurationWidget(HaskellBuildConfiguration *bc)
- : NamedWidget()
+ : NamedWidget(tr("General"))
, m_buildConfiguration(bc)
{
- setDisplayName(tr("General"));
setLayout(new QVBoxLayout);
- layout()->setMargin(0);
+ layout()->setContentsMargins(0, 0, 0, 0);
auto box = new Utils::DetailsWidget;
box->setState(Utils::DetailsWidget::NoSummary);
layout()->addWidget(box);
auto details = new QWidget;
box->setWidget(details);
details->setLayout(new QHBoxLayout);
- details->layout()->setMargin(0);
+ details->layout()->setContentsMargins(0, 0, 0, 0);
details->layout()->addWidget(new QLabel(tr("Build directory:")));
auto buildDirectoryInput = new Utils::PathChooser;
buildDirectoryInput->setExpectedKind(Utils::PathChooser::Directory);
- buildDirectoryInput->setFileName(m_buildConfiguration->buildDirectory());
+ buildDirectoryInput->setFilePath(m_buildConfiguration->buildDirectory());
details->layout()->addWidget(buildDirectoryInput);
connect(m_buildConfiguration,
&BuildConfiguration::buildDirectoryChanged,
buildDirectoryInput,
[this, buildDirectoryInput] {
- buildDirectoryInput->setFileName(m_buildConfiguration->buildDirectory());
+ buildDirectoryInput->setFilePath(m_buildConfiguration->buildDirectory());
});
connect(buildDirectoryInput,
- &Utils::PathChooser::pathChanged,
+ &Utils::PathChooser::textChanged,
m_buildConfiguration,
[this, buildDirectoryInput](const QString &) {
- m_buildConfiguration->setBuildDirectory(buildDirectoryInput->rawFileName());
+ m_buildConfiguration->setBuildDirectory(buildDirectoryInput->rawFilePath());
});
}
diff --git a/plugins/haskell/haskellbuildconfiguration.h b/plugins/haskell/haskellbuildconfiguration.h
index c45c35f..c93161e 100644
--- a/plugins/haskell/haskellbuildconfiguration.h
+++ b/plugins/haskell/haskellbuildconfiguration.h
@@ -33,15 +33,8 @@ namespace Internal {
class HaskellBuildConfigurationFactory : public ProjectExplorer::BuildConfigurationFactory
{
- Q_OBJECT
-
public:
HaskellBuildConfigurationFactory();
-
- QList<ProjectExplorer::BuildInfo> availableBuilds(
- const ProjectExplorer::Target *parent) const override;
- QList<ProjectExplorer::BuildInfo> availableSetups(const ProjectExplorer::Kit *k,
- const QString &projectPath) const override;
};
class HaskellBuildConfiguration : public ProjectExplorer::BuildConfiguration
@@ -49,12 +42,11 @@ class HaskellBuildConfiguration : public ProjectExplorer::BuildConfiguration
Q_OBJECT
public:
- HaskellBuildConfiguration(ProjectExplorer::Target *target, Core::Id id);
+ HaskellBuildConfiguration(ProjectExplorer::Target *target, Utils::Id id);
ProjectExplorer::NamedWidget *createConfigWidget() override;
BuildType buildType() const override;
void setBuildType(BuildType type);
- void initialize(const ProjectExplorer::BuildInfo &info) override;
private:
BuildType m_buildType = BuildType::Release;
diff --git a/plugins/haskell/haskellcompletionassist.cpp b/plugins/haskell/haskellcompletionassist.cpp
deleted file mode 100644
index aa053f5..0000000
--- a/plugins/haskell/haskellcompletionassist.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-****************************************************************************/
-
-#include "haskellcompletionassist.h"
-
-#include "haskellconstants.h"
-
-#include <coreplugin/id.h>
-#include <texteditor/codeassist/keywordscompletionassist.h>
-
-namespace Haskell {
-namespace Internal {
-
-TextEditor::IAssistProcessor *HaskellCompletionAssistProvider::createProcessor() const
-{
- auto processor = new TextEditor::KeywordsCompletionAssistProcessor(TextEditor::Keywords());
- processor->setSnippetGroup(Constants::C_HASKELLSNIPPETSGROUP_ID);
- return processor;
-}
-
-} // namespace Internal
-} // namespace Haskell
diff --git a/plugins/haskell/haskellcompletionassist.h b/plugins/haskell/haskellcompletionassist.h
deleted file mode 100644
index 9b3f0ca..0000000
--- a/plugins/haskell/haskellcompletionassist.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-****************************************************************************/
-
-#ifndef HASKELLCOMPLETIONASSIST_H
-#define HASKELLCOMPLETIONASSIST_H
-
-#include <texteditor/codeassist/completionassistprovider.h>
-
-namespace Haskell {
-namespace Internal {
-
-class HaskellCompletionAssistProvider : public TextEditor::CompletionAssistProvider
-{
- Q_OBJECT
-
-public:
- TextEditor::IAssistProcessor *createProcessor() const override;
-};
-
-} // namespace Internal
-} // namespace Haskell
-
-#endif // HASKELLCOMPLETIONASSIST_H
diff --git a/plugins/haskell/haskellconstants.h b/plugins/haskell/haskellconstants.h
index 23463eb..8b63761 100644
--- a/plugins/haskell/haskellconstants.h
+++ b/plugins/haskell/haskellconstants.h
@@ -32,7 +32,10 @@ const char C_HASKELLEDITOR_ID[] = "Haskell.HaskellEditor";
const char C_HASKELLSNIPPETSGROUP_ID[] = "Haskell";
const char C_HASKELL_PROJECT_MIMETYPE[] = "text/x-haskell-project";
const char C_HASKELL_PROJECT_ID[] = "Haskell.Project";
+const char C_HASKELL_RUNCONFIG_ID[] = "Haskell.RunConfiguration";
+const char C_STACK_BUILD_STEP_ID[] = "Haskell.Stack.Build";
const char OPTIONS_GENERAL[] = "Haskell.A.General";
+const char A_RUN_GHCI[] = "Haskell.RunGHCi";
} // namespace Haskell
} // namespace Constants
diff --git a/plugins/haskell/haskelldocument.cpp b/plugins/haskell/haskelldocument.cpp
deleted file mode 100644
index b2cc8e0..0000000
--- a/plugins/haskell/haskelldocument.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-****************************************************************************/
-
-#include "haskelldocument.h"
-
-#include "haskellconstants.h"
-#include "haskellmanager.h"
-
-using namespace TextEditor;
-using namespace Utils;
-
-namespace Haskell {
-namespace Internal {
-
-HaskellDocument::HaskellDocument()
- : TextDocument(Constants::C_HASKELLEDITOR_ID)
-{
- connect(this, &IDocument::filePathChanged, this, [this](const FilePath &, const FilePath &fn) {
- m_ghcmod = HaskellManager::ghcModForFile(fn);
- });
-}
-
-std::shared_ptr<AsyncGhcMod> HaskellDocument::ghcMod() const
-{
- return m_ghcmod;
-}
-
-} // namespace Internal
-} // namespace Haskell
diff --git a/plugins/haskell/haskelldocument.h b/plugins/haskell/haskelldocument.h
deleted file mode 100644
index 72af5e9..0000000
--- a/plugins/haskell/haskelldocument.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-****************************************************************************/
-
-#pragma once
-
-#include <texteditor/textdocument.h>
-
-#include <memory>
-
-namespace Haskell {
-namespace Internal {
-
-class AsyncGhcMod;
-
-class HaskellDocument : public TextEditor::TextDocument
-{
- Q_OBJECT
-
-public:
- HaskellDocument();
-
- std::shared_ptr<AsyncGhcMod> ghcMod() const;
-
-private:
- std::shared_ptr<AsyncGhcMod> m_ghcmod;
-};
-
-} // namespace Internal
-} // namespace Haskell
diff --git a/plugins/haskell/haskelleditorfactory.cpp b/plugins/haskell/haskelleditorfactory.cpp
index 8e5d837..2066779 100644
--- a/plugins/haskell/haskelleditorfactory.cpp
+++ b/plugins/haskell/haskelleditorfactory.cpp
@@ -25,21 +25,32 @@
#include "haskelleditorfactory.h"
-#include "haskellcompletionassist.h"
#include "haskellconstants.h"
-#include "haskelldocument.h"
-#include "haskelleditorwidget.h"
#include "haskellhighlighter.h"
-#include "haskellhoverhandler.h"
+#include "haskellmanager.h"
+#include <coreplugin/actionmanager/commandbutton.h>
#include <texteditor/textdocument.h>
#include <texteditor/texteditoractionhandler.h>
+#include <texteditor/textindenter.h>
#include <QCoreApplication>
namespace Haskell {
namespace Internal {
+static QWidget *createEditorWidget()
+{
+ auto widget = new TextEditor::TextEditorWidget;
+ auto ghciButton = new Core::CommandButton(Constants::A_RUN_GHCI, widget);
+ ghciButton->setText(HaskellManager::tr("GHCi"));
+ QObject::connect(ghciButton, &QToolButton::clicked, HaskellManager::instance(), [widget] {
+ HaskellManager::openGhci(widget->textDocument()->filePath());
+ });
+ widget->insertExtraToolBarWidget(TextEditor::TextEditorWidget::Left, ghciButton);
+ return widget;
+}
+
HaskellEditorFactory::HaskellEditorFactory()
{
setId(Constants::C_HASKELLEDITOR_ID);
@@ -47,13 +58,12 @@ HaskellEditorFactory::HaskellEditorFactory()
addMimeType("text/x-haskell");
setEditorActionHandlers(TextEditor::TextEditorActionHandler::UnCommentSelection
| TextEditor::TextEditorActionHandler::FollowSymbolUnderCursor);
- addHoverHandler(new HaskellHoverHandler);
- setDocumentCreator([] { return new HaskellDocument(); });
- setEditorWidgetCreator([] { return new HaskellEditorWidget; });
+ setDocumentCreator([] { return new TextEditor::TextDocument(Constants::C_HASKELLEDITOR_ID); });
+ setIndenterCreator([](QTextDocument *doc) { return new TextEditor::TextIndenter(doc); });
+ setEditorWidgetCreator(createEditorWidget);
setCommentDefinition(Utils::CommentDefinition("--", "{-", "-}"));
setParenthesesMatchingEnabled(true);
setMarksVisible(true);
- setCompletionAssistProvider(new HaskellCompletionAssistProvider);
setSyntaxHighlighterCreator([] { return new HaskellHighlighter(); });
}
diff --git a/plugins/haskell/haskelleditorwidget.cpp b/plugins/haskell/haskelleditorwidget.cpp
deleted file mode 100644
index d4154ae..0000000
--- a/plugins/haskell/haskelleditorwidget.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-****************************************************************************/
-
-#include "haskelleditorwidget.h"
-
-#include "haskellconstants.h"
-#include "haskelltokenizer.h"
-
-#include <coreplugin/icore.h>
-#include <coreplugin/infobar.h>
-#include <texteditor/textdocument.h>
-#include <utils/textutils.h>
-
-#include <QTextBlock>
-#include <QTimer>
-
-using namespace TextEditor;
-
-namespace Haskell {
-namespace Internal {
-
-HaskellEditorWidget::HaskellEditorWidget(QWidget *parent)
- : TextEditorWidget(parent)
-{
-}
-
-Utils::optional<Token> HaskellEditorWidget::symbolAt(QTextDocument *doc, int position,
- int *line, int *column)
-{
- Utils::Text::convertPosition(doc, position, line, column);
- if (*line < 0 || *column < 0)
- return Utils::nullopt;
- const QTextBlock block = doc->findBlockByNumber(*line - 1);
- if (block.text().isEmpty())
- return Utils::nullopt;
- const int startState = block.previous().isValid() ? block.previous().userState() : -1;
- const Tokens tokens = HaskellTokenizer::tokenize(block.text(), startState);
- const Token token = tokens.tokenAtColumn(*column);
- if (token.isValid()
- && (token.type == TokenType::Variable
- || token.type == TokenType::Operator
- || token.type == TokenType::Constructor
- || token.type == TokenType::OperatorConstructor)) {
- return token;
- }
- return Utils::nullopt;
-}
-
-void HaskellEditorWidget::showFailedToStartStackError(const QString &stackExecutable,
- TextEditorWidget *widget)
-{
- static const char id[] = "Haskell.FailedToStartStack";
- Core::IDocument *document = widget->textDocument();
- if (!document->infoBar()->containsInfo(id)) {
- Core::InfoBarEntry info(
- id,
- tr("Failed to start Haskell Stack \"%1\". Make sure you have stack installed and configured in the options.")
- .arg(stackExecutable));
- info.setCustomButtonInfo(Core::ICore::msgShowOptionsDialog(), [document] {
- QTimer::singleShot(0, Core::ICore::instance(), [document] {
- document->infoBar()->removeInfo(id);
- Core::ICore::showOptionsDialog(Constants::OPTIONS_GENERAL);
- });
- });
- document->infoBar()->addInfo(info);
- }
-}
-
-void HaskellEditorWidget::findLinkAt(const QTextCursor &cursor,
- Utils::ProcessLinkCallback &&processLinkCallback,
- bool resolveTarget,
- bool inNextSplit)
-{
- Utils::Link link;
- int line, column;
- const Utils::optional<Token> symbol = symbolAt(document(), cursor.position(), &line, &column);
- if (symbol) {
- const QTextBlock block = document()->findBlockByNumber(line - 1);
- Utils::Link link;
- link.linkTextStart = block.position() + symbol->startCol;
- link.linkTextEnd = link.linkTextStart + symbol->length;
- if (resolveTarget) {
- m_followSymbolAssistProvider.setOpenInNextSplit(inNextSplit);
- invokeAssist(FollowSymbol, &m_followSymbolAssistProvider);
- }
- }
- processLinkCallback(link);
-}
-
-} // namespace Internal
-} // namespace Haskell
diff --git a/plugins/haskell/haskelleditorwidget.h b/plugins/haskell/haskelleditorwidget.h
deleted file mode 100644
index 24af561..0000000
--- a/plugins/haskell/haskelleditorwidget.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-****************************************************************************/
-
-#pragma once
-
-#include "followsymbol.h"
-
-#include <texteditor/texteditor.h>
-#include <utils/optional.h>
-
-namespace Haskell {
-namespace Internal {
-
-class Token;
-
-class HaskellEditorWidget : public TextEditor::TextEditorWidget
-{
-public:
- HaskellEditorWidget(QWidget *parent = 0);
-
- static Utils::optional<Token> symbolAt(QTextDocument *doc, int position,
- int *line, int *column);
-
- static void showFailedToStartStackError(const QString &stackExecutable,
- TextEditor::TextEditorWidget *widget);
-
-protected:
- void findLinkAt(const QTextCursor &cursor,
- Utils::ProcessLinkCallback &&processLinkCallback,
- bool resolveTarget = true,
- bool inNextSplit = false) override;
-
-private:
- FollowSymbolAssistProvider m_followSymbolAssistProvider;
-};
-
-} // namespace Internal
-} // namespace Haskell
diff --git a/plugins/haskell/haskellhighlighter.cpp b/plugins/haskell/haskellhighlighter.cpp
index 9899cc4..8a5b6ca 100644
--- a/plugins/haskell/haskellhighlighter.cpp
+++ b/plugins/haskell/haskellhighlighter.cpp
@@ -80,10 +80,10 @@ void HaskellHighlighter::highlightBlock(const QString &text)
setTokenFormat(token, C_VISUAL_WHITESPACE);
break;
case TokenType::Keyword:
- if (token.text == "::" && firstNonWS && !secondNonWS) { // toplevel declaration
+ if (token.text == QLatin1String("::") && firstNonWS && !secondNonWS) { // toplevel declaration
setFormat(firstNonWS->startCol, firstNonWS->length, m_toplevelDeclFormat);
inType = true;
- } else if (token.text == "import") {
+ } else if (token.text == QLatin1String("import")) {
inImport = true;
}
setTokenFormat(token, C_KEYWORD);
diff --git a/plugins/haskell/haskellhoverhandler.cpp b/plugins/haskell/haskellhoverhandler.cpp
deleted file mode 100644
index 1719297..0000000
--- a/plugins/haskell/haskellhoverhandler.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-****************************************************************************/
-
-#include "haskellhoverhandler.h"
-
-#include "haskelldocument.h"
-#include "haskelleditorwidget.h"
-#include "haskellmanager.h"
-#include "haskelltokenizer.h"
-
-#include <texteditor/textdocument.h>
-#include <texteditor/texteditor.h>
-#include <utils/qtcassert.h>
-#include <utils/runextensions.h>
-#include <utils/synchronousprocess.h>
-#include <utils/tooltip/tooltip.h>
-
-using namespace Utils;
-
-static QString toCode(const QString &s)
-{
- if (s.isEmpty())
- return s;
- return "<pre>" + s.toHtmlEscaped() + "</pre>";
-}
-
-namespace Haskell {
-namespace Internal {
-
-QString symbolToHtml(const SymbolInfo &info)
-{
- if (info.definition.isEmpty())
- return QString();
- QString result = "<pre>" + info.definition.join("\n") + "</pre>";
- if (!info.file.isEmpty()) {
- result += "<div align=\"right\"><i>-- " + info.file.toString();
- if (info.line >= 0) {
- result += ":" + QString::number(info.line);
- if (info.col >= 0)
- result += ":" + QString::number(info.col);
- }
- result += "</i></div>";
- } else if (!info.module.isEmpty()) {
- result += "<div align=\"right\"><i>-- Module \"" + info.module + "\"</i></div>";
- }
- if (!info.additionalInfo.isEmpty())
- result += "<pre>" + info.additionalInfo.join("\n") + "</pre>";
- return result;
-}
-
-void HaskellHoverHandler::identifyMatch(TextEditor::TextEditorWidget *editorWidget,
- int pos,
- ReportPriority report)
-{
- cancel();
- m_name.clear();
- m_filePath.clear();
- const Utils::optional<Token> token = HaskellEditorWidget::symbolAt(editorWidget->document(),
- pos, &m_line, &m_col);
- if (token) {
- m_filePath = editorWidget->textDocument()->filePath();
- m_name = token->text.toString();
- setPriority(Priority_Tooltip);
- } else {
- setPriority(-1);
- }
- report(priority());
-}
-
-static void showError(const QPointer<TextEditor::TextEditorWidget> &widget,
- const Error &typeError, const Error &infoError)
-{
- if (typeError.type == Error::Type::FailedToStartStack
- || infoError.type == Error::Type::FailedToStartStack) {
- const QString stackExecutable = typeError.type == Error::Type::FailedToStartStack
- ? typeError.details
- : infoError.details;
- HaskellEditorWidget::showFailedToStartStackError(stackExecutable, widget);
- }
-}
-
-static void tryShowToolTip(const QPointer<TextEditor::TextEditorWidget> &widget, const QPoint &point,
- QFuture<QStringOrError> typeFuture,
- QFuture<SymbolInfoOrError> symbolFuture)
-{
- if (Utils::ToolTip::isVisible() && widget
- && symbolFuture.isResultReadyAt(0) && typeFuture.isResultReadyAt(0)) {
- const QStringOrError typeOrError = typeFuture.result();
- const SymbolInfoOrError infoOrError = symbolFuture.result();
- if (const Error *typeError = Utils::get_if<Error>(&typeOrError))
- if (const Error *infoError = Utils::get_if<Error>(&infoOrError))
- showError(widget, *typeError, *infoError);
- const QString *type = Utils::get_if<QString>(&typeOrError);
- const SymbolInfo *info = Utils::get_if<SymbolInfo>(&infoOrError);
- const QString typeString = !type || type->isEmpty()
- ? QString()
- : toCode(":: " + *type);
- const QString infoString = info ? symbolToHtml(*info) : QString();
- const QString tip = typeString + infoString;
- Utils::ToolTip::show(point, tip, widget);
- }
-}
-
-void HaskellHoverHandler::operateTooltip(TextEditor::TextEditorWidget *editorWidget,
- const QPoint &point)
-{
- cancel();
- if (m_name.isEmpty()) {
- Utils::ToolTip::hide();
- return;
- }
- Utils::ToolTip::show(point, HaskellManager::trLookingUp(m_name), editorWidget);
-
- QPointer<TextEditor::TextEditorWidget> widget = editorWidget;
- std::shared_ptr<AsyncGhcMod> ghcmod;
- auto doc = qobject_cast<HaskellDocument *>(editorWidget->textDocument());
- QTC_ASSERT(doc, return);
- ghcmod = doc->ghcMod();
- m_typeFuture = ghcmod->typeInfo(m_filePath, m_line, m_col);
- m_symbolFuture = ghcmod->findSymbol(m_filePath, m_name);
- Utils::onResultReady(m_typeFuture,
- [typeFuture = m_typeFuture, symbolFuture = m_symbolFuture,
- ghcmod, widget, point] // hold shared ghcmod pointer
- (const QStringOrError &) {
- tryShowToolTip(widget, point, typeFuture, symbolFuture);
- });
- Utils::onResultReady(m_symbolFuture,
- [typeFuture = m_typeFuture, symbolFuture = m_symbolFuture,
- ghcmod, widget, point] // hold shared ghcmod pointer
- (const SymbolInfoOrError &) {
- tryShowToolTip(widget, point, typeFuture, symbolFuture);
- });
-}
-
-void HaskellHoverHandler::cancel()
-{
- if (m_symbolFuture.isRunning())
- m_symbolFuture.cancel();
- if (m_typeFuture.isRunning())
- m_typeFuture.cancel();
-}
-
-} // namespace Internal
-} // namespace Haskell
diff --git a/plugins/haskell/haskellhoverhandler.h b/plugins/haskell/haskellhoverhandler.h
deleted file mode 100644
index a85da8e..0000000
--- a/plugins/haskell/haskellhoverhandler.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-****************************************************************************/
-
-#pragma once
-
-#include "ghcmod.h"
-
-#include <texteditor/basehoverhandler.h>
-#include <utils/fileutils.h>
-#include <utils/optional.h>
-
-namespace Haskell {
-namespace Internal {
-
-class HaskellHoverHandler : public TextEditor::BaseHoverHandler
-{
-private:
- void identifyMatch(TextEditor::TextEditorWidget *editorWidget,
- int pos,
- ReportPriority report) override;
- void operateTooltip(TextEditor::TextEditorWidget *editorWidget, const QPoint &point) override;
-
- void cancel();
-
- Utils::FilePath m_filePath;
- int m_line = -1;
- int m_col = -1;
- QString m_name;
-
- QFuture<SymbolInfoOrError> m_symbolFuture;
- QFuture<QStringOrError> m_typeFuture;
-};
-
-} // namespace Internal
-} // namespace Haskell
diff --git a/plugins/haskell/haskellmanager.cpp b/plugins/haskell/haskellmanager.cpp
index a2eadc6..dd6a371 100644
--- a/plugins/haskell/haskellmanager.cpp
+++ b/plugins/haskell/haskellmanager.cpp
@@ -25,9 +25,13 @@
#include "haskellmanager.h"
-#include "ghcmod.h"
-
+#include <coreplugin/messagemanager.h>
+#include <utils/algorithm.h>
+#include <utils/commandline.h>
#include <utils/hostosinfo.h>
+#include <utils/mimeutils.h>
+#include <utils/processenums.h>
+#include <utils/qtcprocess.h>
#include <QCoreApplication>
#include <QDir>
@@ -46,7 +50,6 @@ namespace Internal {
class HaskellManagerPrivate
{
public:
- std::unordered_map<FilePath, std::weak_ptr<AsyncGhcMod>> ghcModCache;
FilePath stackExecutable;
};
@@ -74,21 +77,6 @@ FilePath HaskellManager::findProjectDirectory(const FilePath &filePath)
return {};
}
-std::shared_ptr<AsyncGhcMod> HaskellManager::ghcModForFile(const FilePath &filePath)
-{
- const FilePath projectPath = findProjectDirectory(filePath);
- const auto cacheEntry = m_d->ghcModCache.find(projectPath);
- if (cacheEntry != m_d->ghcModCache.cend()) {
- if (cacheEntry->second.expired())
- m_d->ghcModCache.erase(cacheEntry);
- else
- return cacheEntry->second.lock();
- }
- auto ghcmod = std::make_shared<AsyncGhcMod>(projectPath);
- m_d->ghcModCache.insert(std::make_pair(projectPath, ghcmod));
- return ghcmod;
-}
-
FilePath defaultStackExecutable()
{
// stack from brew or the installer script from https://docs.haskellstack.org
@@ -111,6 +99,28 @@ void HaskellManager::setStackExecutable(const FilePath &filePath)
emit m_instance->stackExecutableChanged(m_d->stackExecutable);
}
+void HaskellManager::openGhci(const FilePath &haskellFile)
+{
+ const QList<MimeType> mimeTypes = mimeTypesForFileName(haskellFile.toString());
+ const bool isHaskell = Utils::anyOf(mimeTypes, [](const MimeType &mt) {
+ return mt.inherits("text/x-haskell") || mt.inherits("text/x-literate-haskell");
+ });
+ const auto args = QStringList{"ghci"}
+ + (isHaskell ? QStringList{haskellFile.fileName()} : QStringList());
+ auto p = new QtcProcess(m_instance);
+ p->setTerminalMode(TerminalMode::On);
+ p->setCommand({stackExecutable(), args});
+ p->setWorkingDirectory(haskellFile.absolutePath());
+ connect(p, &QtcProcess::done, p, [p] {
+ if (p->result() != ProcessResult::FinishedWithSuccess) {
+ Core::MessageManager::writeDisrupting(
+ tr("Failed to run GHCi: \"%1\".").arg(p->errorString()));
+ }
+ p->deleteLater();
+ });
+ p->start();
+}
+
void HaskellManager::readSettings(QSettings *settings)
{
m_d->stackExecutable = FilePath::fromString(
@@ -127,10 +137,5 @@ void HaskellManager::writeSettings(QSettings *settings)
settings->setValue(kStackExecutableKey, m_d->stackExecutable.toString());
}
-QString HaskellManager::trLookingUp(const QString &name)
-{
- return QCoreApplication::translate("HaskellManager", "Looking up \"%1\"...").arg(name);
-}
-
} // namespace Internal
} // namespace Haskell
diff --git a/plugins/haskell/haskellmanager.h b/plugins/haskell/haskellmanager.h
index b4a2e35..361b8f8 100644
--- a/plugins/haskell/haskellmanager.h
+++ b/plugins/haskell/haskellmanager.h
@@ -36,8 +36,6 @@ QT_END_NAMESPACE
namespace Haskell {
namespace Internal {
-class AsyncGhcMod;
-
class HaskellManager : public QObject
{
Q_OBJECT
@@ -46,14 +44,12 @@ public:
static HaskellManager *instance();
static Utils::FilePath findProjectDirectory(const Utils::FilePath &filePath);
- static std::shared_ptr<AsyncGhcMod> ghcModForFile(const Utils::FilePath &filePath);
static Utils::FilePath stackExecutable();
static void setStackExecutable(const Utils::FilePath &filePath);
+ static void openGhci(const Utils::FilePath &haskellFile);
static void readSettings(QSettings *settings);
static void writeSettings(QSettings *settings);
- static QString trLookingUp(const QString &name);
-
signals:
void stackExecutableChanged(const Utils::FilePath &filePath);
};
diff --git a/plugins/haskell/haskellplugin.cpp b/plugins/haskell/haskellplugin.cpp
index d92c4f8..c3ae400 100644
--- a/plugins/haskell/haskellplugin.cpp
+++ b/plugins/haskell/haskellplugin.cpp
@@ -25,7 +25,6 @@
#include "haskellplugin.h"
-#include "ghcmod.h"
#include "haskellbuildconfiguration.h"
#include "haskellconstants.h"
#include "haskelleditorfactory.h"
@@ -35,10 +34,15 @@
#include "optionspage.h"
#include "stackbuildstep.h"
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <projectexplorer/projectmanager.h>
+#include <projectexplorer/jsonwizard/jsonwizardfactory.h>
#include <texteditor/snippets/snippetprovider.h>
+#include <QAction>
+
namespace Haskell {
namespace Internal {
@@ -50,6 +54,7 @@ public:
HaskellBuildConfigurationFactory buildConfigFactory;
StackBuildStepFactory stackBuildStepFactory;
HaskellRunConfigurationFactory runConfigFactory;
+ ProjectExplorer::SimpleTargetRunnerFactory runWorkerFactory{{Constants::C_HASKELL_RUNCONFIG_ID}};
};
HaskellPlugin::~HaskellPlugin()
@@ -57,6 +62,16 @@ HaskellPlugin::~HaskellPlugin()
delete d;
}
+static void registerGhciAction()
+{
+ QAction *action = new QAction(HaskellManager::tr("Run GHCi"), HaskellManager::instance());
+ Core::ActionManager::registerAction(action, Constants::A_RUN_GHCI);
+ QObject::connect(action, &QAction::triggered, HaskellManager::instance(), [] {
+ if (Core::IDocument *doc = Core::EditorManager::currentDocument())
+ HaskellManager::openGhci(doc->filePath());
+ });
+}
+
bool HaskellPlugin::initialize(const QStringList &arguments, QString *errorString)
{
Q_UNUSED(arguments)
@@ -72,10 +87,12 @@ bool HaskellPlugin::initialize(const QStringList &arguments, QString *errorStrin
connect(Core::ICore::instance(), &Core::ICore::saveSettingsRequested, this, [] {
HaskellManager::writeSettings(Core::ICore::settings());
});
- connect(HaskellManager::instance(), &HaskellManager::stackExecutableChanged,
- &GhcMod::setStackExecutable);
+
+ registerGhciAction();
HaskellManager::readSettings(Core::ICore::settings());
+
+ ProjectExplorer::JsonWizardFactory::addWizardPath(":/haskell/share/wizards/");
return true;
}
diff --git a/plugins/haskell/haskellproject.cpp b/plugins/haskell/haskellproject.cpp
index 92b30e6..248f898 100644
--- a/plugins/haskell/haskellproject.cpp
+++ b/plugins/haskell/haskellproject.cpp
@@ -65,17 +65,12 @@ static QVector<QString> parseExecutableNames(const FilePath &projectFilePath)
return result;
}
-HaskellProjectNode::HaskellProjectNode(const FilePath &projectFilePath)
- : ProjectNode(projectFilePath)
-{}
-
HaskellProject::HaskellProject(const Utils::FilePath &fileName)
- : Project(Constants::C_HASKELL_PROJECT_MIMETYPE, fileName, [this] { refresh(); })
+ : Project(Constants::C_HASKELL_PROJECT_MIMETYPE, fileName)
{
setId(Constants::C_HASKELL_PROJECT_ID);
setDisplayName(fileName.toFileInfo().completeBaseName());
- updateFiles();
- connect(this, &Project::activeTargetChanged, this, &HaskellProject::updateApplicationTargets);
+ setBuildSystemCreator([](Target *t) { return new HaskellBuildSystem(t); });
}
bool HaskellProject::isHaskellProject(Project *project)
@@ -83,34 +78,43 @@ bool HaskellProject::isHaskellProject(Project *project)
return project && project->id() == Constants::C_HASKELL_PROJECT_ID;
}
-void HaskellProject::updateFiles()
+HaskellBuildSystem::HaskellBuildSystem(Target *t)
+ : BuildSystem(t)
{
- emitParsingStarted();
- FilePath projectDir = projectDirectory();
- QFuture<QList<FileNode *>> future = Utils::runAsync([this, projectDir] {
- return FileNode::scanForFiles(projectDir, [this](const FilePath &fn) -> FileNode * {
- if (fn != FilePath::fromString(projectFilePath().toString() + ".user"))
- return new FileNode(fn, FileType::Source);
- else
- return nullptr;
- });
- });
- Utils::onResultReady(future, this, [this](const QList<FileNode *> &nodes) {
- auto root = new HaskellProjectNode(projectDirectory());
- root->setDisplayName(displayName());
+ connect(&m_scanner, &TreeScanner::finished, this, [this] {
+ auto root = std::make_unique<ProjectNode>(projectDirectory());
+ root->setDisplayName(target()->project()->displayName());
std::vector<std::unique_ptr<FileNode>> nodePtrs
- = Utils::transform<std::vector>(nodes, [](FileNode *fn) {
+ = Utils::transform<std::vector>(m_scanner.release().allFiles, [](FileNode *fn) {
return std::unique_ptr<FileNode>(fn);
});
root->addNestedNodes(std::move(nodePtrs));
- setRootProjectNode(std::unique_ptr<ProjectNode>(root));
- emitParsingFinished(true);
+ setRootProjectNode(std::move(root));
+
+ updateApplicationTargets();
+
+ m_parseGuard.markAsSuccess();
+ m_parseGuard = {};
+
+ emitBuildSystemUpdated();
});
+
+ connect(target()->project(),
+ &Project::projectFileIsDirty,
+ this,
+ &BuildSystem::requestDelayedParse);
+
+ requestDelayedParse();
}
-void HaskellProject::updateApplicationTargets(Target *target)
+void HaskellBuildSystem::triggerParsing()
+{
+ m_parseGuard = guardParsingRun();
+ m_scanner.asyncScanForFiles(target()->project()->projectDirectory());
+}
+
+void HaskellBuildSystem::updateApplicationTargets()
{
- QTC_ASSERT(target, return);
const QVector<QString> executables = parseExecutableNames(projectFilePath());
const Utils::FilePath projFilePath = projectFilePath();
const QList<BuildTargetInfo> appTargets
@@ -123,15 +127,8 @@ void HaskellProject::updateApplicationTargets(Target *target)
bti.isQtcRunnable = true;
return bti;
});
- target->setApplicationTargets(appTargets);
- target->updateDefaultRunConfigurations();
-}
-
-void HaskellProject::refresh()
-{
- updateFiles();
- if (activeTarget())
- updateApplicationTargets(activeTarget());
+ setApplicationTargets(appTargets);
+ target()->updateDefaultRunConfigurations();
}
} // namespace Internal
diff --git a/plugins/haskell/haskellproject.h b/plugins/haskell/haskellproject.h
index 0afb717..0ee05d9 100644
--- a/plugins/haskell/haskellproject.h
+++ b/plugins/haskell/haskellproject.h
@@ -25,31 +25,41 @@
#pragma once
+#include <projectexplorer/buildsystem.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectnodes.h>
+#include <projectexplorer/treescanner.h>
namespace Haskell {
namespace Internal {
-class HaskellProjectNode : public ProjectExplorer::ProjectNode
+class HaskellProject : public ProjectExplorer::Project
{
+ Q_OBJECT
+
public:
- HaskellProjectNode(const Utils::FilePath &projectFilePath);
+ explicit HaskellProject(const Utils::FilePath &fileName);
+
+ static bool isHaskellProject(Project *project);
};
-class HaskellProject : public ProjectExplorer::Project
+class HaskellBuildSystem : public ProjectExplorer::BuildSystem
{
Q_OBJECT
public:
- explicit HaskellProject(const Utils::FilePath &fileName);
+ HaskellBuildSystem(ProjectExplorer::Target *t);
- static bool isHaskellProject(Project *project);
+ void triggerParsing() override;
+ QString name() const final { return QLatin1String("haskell"); }
private:
- void updateFiles();
- void updateApplicationTargets(ProjectExplorer::Target *target);
+ void updateApplicationTargets();
void refresh();
+
+private:
+ ParseGuard m_parseGuard;
+ ProjectExplorer::TreeScanner m_scanner;
};
} // namespace Internal
diff --git a/plugins/haskell/haskellrunconfiguration.cpp b/plugins/haskell/haskellrunconfiguration.cpp
index 78ea231..7457471 100644
--- a/plugins/haskell/haskellrunconfiguration.cpp
+++ b/plugins/haskell/haskellrunconfiguration.cpp
@@ -31,7 +31,6 @@
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/localenvironmentaspect.h>
-#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/runconfigurationaspects.h>
#include <projectexplorer/runcontrol.h>
#include <projectexplorer/target.h>
@@ -43,7 +42,7 @@ namespace Internal {
HaskellRunConfigurationFactory::HaskellRunConfigurationFactory()
{
- registerRunConfiguration<HaskellRunConfiguration>("Haskell.RunConfiguration");
+ registerRunConfiguration<HaskellRunConfiguration>(Constants::C_HASKELL_RUNCONFIG_ID);
addSupportedProjectType(Constants::C_HASKELL_PROJECT_ID);
addSupportedTargetDeviceType(ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE);
}
@@ -54,49 +53,43 @@ HaskellExecutableAspect::HaskellExecutableAspect()
setLabelText(tr("Executable"));
}
-HaskellRunConfiguration::HaskellRunConfiguration(Target *target, Core::Id id)
+HaskellRunConfiguration::HaskellRunConfiguration(Target *target, Utils::Id id)
: RunConfiguration(target, id)
{
- addAspect<LocalEnvironmentAspect>(target);
+ auto envAspect = addAspect<LocalEnvironmentAspect>(target);
- auto executableAspect = addAspect<HaskellExecutableAspect>();
- connect(target, &Target::applicationTargetsChanged, this, [this, target, executableAspect] {
- BuildTargetInfo bti = target->buildTarget(buildKey());
- executableAspect->setValue(bti.targetFilePath.toString());
- });
+ addAspect<HaskellExecutableAspect>();
+ addAspect<ArgumentsAspect>(macroExpander());
- addAspect<ArgumentsAspect>();
-
- auto workingDirAspect = addAspect<WorkingDirectoryAspect>();
+ auto workingDirAspect = addAspect<WorkingDirectoryAspect>(macroExpander(), envAspect);
workingDirAspect->setDefaultWorkingDirectory(target->project()->projectDirectory());
workingDirAspect->setVisible(false);
addAspect<TerminalAspect>();
-}
-void HaskellRunConfiguration::doAdditionalSetup(const RunConfigurationCreationInfo &info)
-{
- aspect<HaskellExecutableAspect>()->setValue(info.buildKey);
+ setUpdater([this] { aspect<HaskellExecutableAspect>()->setValue(buildTargetInfo().buildKey); });
+ connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update);
+ update();
}
Runnable HaskellRunConfiguration::runnable() const
{
- const QString projectDirectory = target()->project()->projectDirectory().toString();
+ const Utils::FilePath projectDirectory = target()->project()->projectDirectory();
Runnable r;
- if (BuildConfiguration *buildConfiguration = target()->activeBuildConfiguration())
- r.commandLineArguments += "--work-dir \""
- + QDir(projectDirectory)
- .relativeFilePath(
- buildConfiguration->buildDirectory().toString())
- + "\" ";
- const QString executable = aspect<HaskellExecutableAspect>()->value();
- r.commandLineArguments += "exec \"" + executable + "\"";
- const QString arguments = aspect<ArgumentsAspect>()->arguments(macroExpander());
+ QStringList args;
+ if (BuildConfiguration *buildConfiguration = target()->activeBuildConfiguration()) {
+ args << "--work-dir"
+ << QDir(projectDirectory.toString()).relativeFilePath(
+ buildConfiguration->buildDirectory().toString());
+ }
+ args << "exec" << aspect<HaskellExecutableAspect>()->value();
+ const QString arguments = aspect<ArgumentsAspect>()->arguments();
if (!arguments.isEmpty())
- r.commandLineArguments += " -- " + arguments;
+ args << "--" << arguments;
+
r.workingDirectory = projectDirectory;
r.environment = aspect<LocalEnvironmentAspect>()->environment();
- r.executable = r.environment.searchInPath(HaskellManager::stackExecutable().toString()).toString();
+ r.command = {r.environment.searchInPath(HaskellManager::stackExecutable().toString()), args};
return r;
}
diff --git a/plugins/haskell/haskellrunconfiguration.h b/plugins/haskell/haskellrunconfiguration.h
index 62bbd3d..27e4a83 100644
--- a/plugins/haskell/haskellrunconfiguration.h
+++ b/plugins/haskell/haskellrunconfiguration.h
@@ -25,8 +25,10 @@
#pragma once
+#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/runconfigurationaspects.h>
#include <projectexplorer/runcontrol.h>
+#include <utils/aspects.h>
namespace Haskell {
namespace Internal {
@@ -36,10 +38,9 @@ class HaskellRunConfiguration : public ProjectExplorer::RunConfiguration
Q_OBJECT
public:
- HaskellRunConfiguration(ProjectExplorer::Target *target, Core::Id id);
+ HaskellRunConfiguration(ProjectExplorer::Target *target, Utils::Id id);
private:
- void doAdditionalSetup(const ProjectExplorer::RunConfigurationCreationInfo &info) final;
ProjectExplorer::Runnable runnable() const final;
};
@@ -47,14 +48,9 @@ class HaskellRunConfigurationFactory : public ProjectExplorer::RunConfigurationF
{
public:
HaskellRunConfigurationFactory();
-
-private:
- ProjectExplorer::SimpleRunWorkerFactory<ProjectExplorer::SimpleTargetRunner,
- HaskellRunConfiguration>
- runWorkerFactory;
};
-class HaskellExecutableAspect : public ProjectExplorer::BaseStringAspect
+class HaskellExecutableAspect : public Utils::StringAspect
{
Q_OBJECT
diff --git a/plugins/haskell/haskelltokenizer.cpp b/plugins/haskell/haskelltokenizer.cpp
index b59b48b..6f2556d 100644
--- a/plugins/haskell/haskelltokenizer.cpp
+++ b/plugins/haskell/haskelltokenizer.cpp
@@ -142,7 +142,7 @@ namespace Internal {
Token token(TokenType type, std::shared_ptr<QString> line, int start, int end)
{
- return {type, start, end - start, line->midRef(start, end - start), line};
+ return {type, start, end - start, QStringView(*line).mid(start, end - start), line};
}
Tokens::Tokens(std::shared_ptr<QString> source)
@@ -251,7 +251,7 @@ static QVector<Token> getSpace(std::shared_ptr<QString> line, int start)
++current;
const int length = int(std::distance(tokenStart, current));
if (current > tokenStart)
- return {{TokenType::Whitespace, start, length, line->midRef(start, length), line}};
+ return {{TokenType::Whitespace, start, length, QStringView(*line).mid(start, length), line}};
return {};
}
@@ -405,7 +405,7 @@ static int getEscape(const QString &line, int start)
return 0;
return count + 1;
}
- const QStringRef s = line.midRef(start);
+ const QStringView s = QStringView(line).mid(start);
for (const QString &esc : *ASCII_ESCAPES) {
if (s.startsWith(esc))
return esc.length();
@@ -481,7 +481,7 @@ static QVector<Token> getString(std::shared_ptr<QString> line, int start, bool *
lastRef.type = TokenType::StringError;
} else {
--lastRef.length;
- lastRef.text = line->midRef(lastRef.startCol, lastRef.length);
+ lastRef.text = QStringView(*line).mid(lastRef.startCol, lastRef.length);
result.append(token(TokenType::StringError, line, current - 1, current));
}
}
@@ -496,11 +496,11 @@ static QVector<Token> getMultiLineComment(std::shared_ptr<QString> line, int sta
const int length = line->length();
int current = start;
do {
- const QStringRef test = line->midRef(current, 2);
- if (test == "{-") {
+ const QStringView test = QStringView(*line).mid(current, 2);
+ if (test == QLatin1String("{-")) {
++(*commentLevel);
current += 2;
- } else if (test == "-}" && *commentLevel > 0) {
+ } else if (test == QLatin1String("-}") && *commentLevel > 0) {
--(*commentLevel);
current += 2;
} else if (*commentLevel > 0) {
@@ -587,7 +587,7 @@ static QVector<Token> getChar(std::shared_ptr<QString> line, int start)
static QVector<Token> getSpecial(std::shared_ptr<QString> line, int start)
{
if (SPECIAL->contains(line->at(start)))
- return {{TokenType::Special, start, 1, line->midRef(start, 1), line}};
+ return {{TokenType::Special, start, 1, QStringView(*line).mid(start, 1), line}};
return {};
}
@@ -620,7 +620,7 @@ Tokens HaskellTokenizer::tokenize(const QString &line, int startState)
tokens = {{TokenType::Unknown,
currentStart,
1,
- result.source->midRef(currentStart, 1),
+ QStringView(*result.source).mid(currentStart, 1),
result.source}};
result.append(tokens);
}
diff --git a/plugins/haskell/haskelltokenizer.h b/plugins/haskell/haskelltokenizer.h
index 29b10df..63d82d9 100644
--- a/plugins/haskell/haskelltokenizer.h
+++ b/plugins/haskell/haskelltokenizer.h
@@ -61,7 +61,7 @@ public:
TokenType type = TokenType::Unknown;
int startCol = -1;
int length = -1;
- QStringRef text;
+ QStringView text;
std::shared_ptr<QString> source; // keep the string ref alive
};
diff --git a/plugins/haskell/optionspage.cpp b/plugins/haskell/optionspage.cpp
index 04cab79..63d1594 100644
--- a/plugins/haskell/optionspage.cpp
+++ b/plugins/haskell/optionspage.cpp
@@ -62,7 +62,7 @@ QWidget *OptionsPage::widget()
m_stackPath = new PathChooser();
m_stackPath->setExpectedKind(PathChooser::ExistingCommand);
m_stackPath->setPromptDialogTitle(tr("Choose Stack Executable"));
- m_stackPath->setFileName(HaskellManager::stackExecutable());
+ m_stackPath->setFilePath(HaskellManager::stackExecutable());
m_stackPath->setCommandVersionArguments({"--version"});
boxLayout->addWidget(m_stackPath);
}
@@ -73,7 +73,7 @@ void OptionsPage::apply()
{
if (!m_widget)
return;
- HaskellManager::setStackExecutable(m_stackPath->rawFileName());
+ HaskellManager::setStackExecutable(m_stackPath->rawFilePath());
}
void OptionsPage::finish()
diff --git a/plugins/haskell/share/wizards/module/file.hs b/plugins/haskell/share/wizards/module/file.hs
new file mode 100644
index 0000000..e3f85b3
--- /dev/null
+++ b/plugins/haskell/share/wizards/module/file.hs
@@ -0,0 +1 @@
+module %{BaseName} where
diff --git a/plugins/haskell/share/wizards/module/wizard.json b/plugins/haskell/share/wizards/module/wizard.json
new file mode 100644
index 0000000..37f6ebf
--- /dev/null
+++ b/plugins/haskell/share/wizards/module/wizard.json
@@ -0,0 +1,42 @@
+{
+ "version": 1,
+ "supportedProjectTypes": [ ],
+ "id": "C.Module",
+ "category": "S.Haskell",
+ "trDescription": "Creates a Haskell module.",
+ "trDisplayName": "Module",
+ "trDisplayCategory": "Haskell",
+ "iconText": "hs",
+ "enabled": "%{JS: value('Plugins').indexOf('Haskell') >= 0}",
+
+ "options": [
+ { "key": "FileName", "value": "%{JS: Util.fileName(value('TargetPath'), 'hs')}" },
+ { "key": "BaseName", "value": "%{JS: Util.baseName(value('FileName'))}" }
+ ],
+
+ "pages" :
+ [
+ {
+ "trDisplayName": "Location",
+ "trShortTitle": "Location",
+ "typeId": "File"
+ },
+ {
+ "trDisplayName": "Project Management",
+ "trShortTitle": "Summary",
+ "typeId": "Summary"
+ }
+ ],
+ "generators" :
+ [
+ {
+ "typeId": "File",
+ "data":
+ {
+ "source": "file.hs",
+ "target": "%{FileName}",
+ "openInEditor": true
+ }
+ }
+ ]
+}
diff --git a/plugins/haskell/stackbuildstep.cpp b/plugins/haskell/stackbuildstep.cpp
index 931fa32..52ed64f 100644
--- a/plugins/haskell/stackbuildstep.cpp
+++ b/plugins/haskell/stackbuildstep.cpp
@@ -25,6 +25,7 @@
#include "stackbuildstep.h"
+#include "haskellconstants.h"
#include "haskellmanager.h"
#include <projectexplorer/buildconfiguration.h>
@@ -34,42 +35,18 @@
using namespace ProjectExplorer;
-const char C_STACK_BUILD_STEP_ID[] = "Haskell.Stack.Build";
-
namespace Haskell {
namespace Internal {
-StackBuildStep::StackBuildStep(ProjectExplorer::BuildStepList *bsl)
- : AbstractProcessStep(bsl, C_STACK_BUILD_STEP_ID)
+StackBuildStep::StackBuildStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id)
+ : AbstractProcessStep(bsl, id)
{
setDefaultDisplayName(trDisplayName());
-
- const auto updateArguments = [this] {
- const auto projectDir = QDir(project()->projectDirectory().toString());
- processParameters()->setArguments(
- "build --work-dir \""
- + projectDir.relativeFilePath(buildConfiguration()->buildDirectory().toString()) + "\"");
- };
- const auto updateEnvironment = [this] {
- processParameters()->setEnvironment(buildConfiguration()->environment());
- };
- processParameters()->setCommand(HaskellManager::stackExecutable());
- updateArguments();
- processParameters()->setWorkingDirectory(project()->projectDirectory());
- updateEnvironment();
- connect(HaskellManager::instance(),
- &HaskellManager::stackExecutableChanged,
- this,
- [this](const Utils::FilePath &stackExe) {
- processParameters()->setCommand(stackExe);
- });
- connect(buildConfiguration(), &BuildConfiguration::buildDirectoryChanged, this, updateArguments);
- connect(buildConfiguration(), &BuildConfiguration::environmentChanged, this, updateEnvironment);
}
-BuildStepConfigWidget *StackBuildStep::createConfigWidget()
+QWidget *StackBuildStep::createConfigWidget()
{
- return new BuildStepConfigWidget(this);
+ return new QWidget;
}
QString StackBuildStep::trDisplayName()
@@ -77,9 +54,21 @@ QString StackBuildStep::trDisplayName()
return tr("Stack Build");
}
+bool StackBuildStep::init()
+{
+ if (AbstractProcessStep::init()) {
+ const auto projectDir = QDir(project()->projectDirectory().toString());
+ processParameters()->setCommandLine(
+ {HaskellManager::stackExecutable(),
+ {"build", "--work-dir", projectDir.relativeFilePath(buildDirectory().toString())}});
+ processParameters()->setEnvironment(buildEnvironment());
+ }
+ return true;
+}
+
StackBuildStepFactory::StackBuildStepFactory()
{
- registerStep<StackBuildStep>(C_STACK_BUILD_STEP_ID);
+ registerStep<StackBuildStep>(Constants::C_STACK_BUILD_STEP_ID);
setDisplayName(StackBuildStep::StackBuildStep::trDisplayName());
setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_BUILD);
}
diff --git a/plugins/haskell/stackbuildstep.h b/plugins/haskell/stackbuildstep.h
index e03ab97..d74d155 100644
--- a/plugins/haskell/stackbuildstep.h
+++ b/plugins/haskell/stackbuildstep.h
@@ -35,11 +35,14 @@ class StackBuildStep : public ProjectExplorer::AbstractProcessStep
Q_OBJECT
public:
- StackBuildStep(ProjectExplorer::BuildStepList *bsl);
+ StackBuildStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id);
- ProjectExplorer::BuildStepConfigWidget *createConfigWidget() override;
+ QWidget *createConfigWidget() override;
static QString trDisplayName();
+
+protected:
+ bool init() override;
};
class StackBuildStepFactory : public ProjectExplorer::BuildStepFactory
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
deleted file mode 100644
index 200dc2f..0000000
--- a/tests/auto/auto.pro
+++ /dev/null
@@ -1,2 +0,0 @@
-TEMPLATE = subdirs
-SUBDIRS += tokenizer
diff --git a/tests/auto/tokenizer/CMakeLists.txt b/tests/auto/tokenizer/CMakeLists.txt
new file mode 100644
index 0000000..90474ca
--- /dev/null
+++ b/tests/auto/tokenizer/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_qtc_test(tst_tokenizer
+ DEPENDS Qt5::Core Qt5::Test
+ INCLUDES ../../../plugins/haskell
+ SOURCES
+ tst_tokenizer.cpp
+ ../../../plugins/haskell/haskelltokenizer.cpp
+ ../../../plugins/haskell/haskelltokenizer.h
+)
diff --git a/tests/auto/tokenizer/tokenizer.pro b/tests/auto/tokenizer/tokenizer.pro
deleted file mode 100644
index a9ec439..0000000
--- a/tests/auto/tokenizer/tokenizer.pro
+++ /dev/null
@@ -1,11 +0,0 @@
-include(../../../plugins/haskell/config.pri)
-
-include($$IDE_SOURCE_TREE/tests/auto/qttest.pri)
-
-SOURCES += tst_tokenizer.cpp \
- $$PWD/../../../plugins/haskell/haskelltokenizer.cpp
-
-HEADERS += \
- $$PWD/../../../plugins/haskell/haskelltokenizer.h
-
-INCLUDEPATH += $$PWD/../../../plugins/haskell