diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 635590b53..277496c14 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,11 +24,19 @@ jobs: build: strategy: matrix: - os: [ ubuntu-20.04, windows-latest, macos-latest ] + os: [ ubuntu-latest, macos-latest, windows-latest ] + include: + - os: ubuntu-latest + image: "debian:10" fail-fast: false runs-on: ${{ matrix.os }} + container: ${{ matrix.image }} steps: + - name: Install build dependencies - Linux + if: ${{ runner.os == 'Linux' }} + run: apt-get update && apt-get install -y git python3 python3-pip make gcc g++ libx11-dev libxkbfile-dev pkg-config libsecret-1-dev rpm xvfb ffmpeg zstd + - name: Checkout the latest code uses: actions/checkout@v3 @@ -38,6 +46,10 @@ jobs: node-version: ${{ env.NODE_VERSION }} - name: Setup Python + # actions/setup-python's copy of Python is compiled for too new glibc, + # which won't work in a Debian 10 Docker image. + # Get Python from apt repos instead on Linux. + if: ${{ runner.os != 'Linux' }} uses: actions/setup-python@v4 with: python-version: ${{ env.PYTHON_VERSION }} @@ -48,11 +60,23 @@ jobs: # out of the box. 'setuptools' package provides 'distutils'. run: python3 -m pip install setuptools + - name: Install Yarn - Linux + if: ${{ runner.os == 'Linux' }} + run: npm install -g yarn + - name: Setup Git Submodule + if: ${{ runner.os != 'Linux' }} run: | git submodule init git submodule update + - name: Setup Git Submodule - Linux + if: ${{ runner.os == 'Linux' }} + run: | + git config --global --add safe.directory /__w/pulsar/pulsar + git submodule init + git submodule update + - name: Check Pulsar Version if: ${{ runner.os != 'Windows' }} run: sed -i -e "s/[0-9]*-dev/`date -u +%Y%m%d%H`/g" package.json @@ -86,6 +110,15 @@ jobs: yarn build yarn run build:apm + - name: Cache Pulsar dependencies - Linux + if: ${{ runner.os == 'Linux' }} + uses: actions/cache/save@v3 + with: + path: | + node_modules + packages + key: Linux-dependencies-${{ github.sha }}-${{ github.workflow }} + # macOS Signing Stuff - name: Build Pulsar Binaries (macOS) (Signed) if: ${{ runner.os == 'macOS' && github.event_name == 'push' }} @@ -135,21 +168,19 @@ jobs: if: ${{ runner.os == 'Windows' }} run: node ./script/rename.js "Windows" + - name: Cache Pulsar Binaries - Linux + if: ${{ runner.os == 'Linux' }} + uses: actions/cache/save@v3 + with: + path: ./binaries + key: pulsar-Linux-Binaries-${{ github.sha }}-${{ github.workflow }} + - name: Upload Binary Artifacts uses: actions/upload-artifact@v3 with: name: ${{ matrix.os }} Binaries path: ./binaries/* - - name: Test Binary - Linux - if: ${{ (runner.os == 'Linux') && env.RUN_LINUX_VT }} - run: | - rm -R node_modules/electron; yarn install --check-files - ./binaries/*AppImage --appimage-extract - export BINARY_NAME='squashfs-root/pulsar' - mkdir -p ./tests/videos - Xvfb -screen 0 1024x768x24+32 :99 & nohup ffmpeg -video_size 1024x768 -f x11grab -i :99.0 ./tests/videos/out.mpg & DISPLAY=:99 PLAYWRIGHT_JUNIT_OUTPUT_NAME=report.xml npx playwright test --reporter=junit,list - - name: Test Binary - Windows if: runner.os == 'Windows' && env.RUN_WINDOWS_VT == true # TODO: Convert script to PowerShell @@ -168,7 +199,7 @@ jobs: PLAYWRIGHT_JUNIT_OUTPUT_NAME=report.xml arch -x86_64 npx playwright test --reporter=junit,list - name: Add binaries to Rolling Release Repo - if: ${{ github.event_name == 'push' }} + if: ${{ github.event_name == 'push' && runner.os != 'Linux' }} # We only want to upload rolling binaries if they are a commit to master # Otherwise we want to not upload if it's a PR or manually triggered build run: | @@ -178,7 +209,67 @@ jobs: - name: Upload Video Artifacts # Run whether this job passed or failed, unless explicitly cancelled. - if: '!cancelled()' + if: ${{ !cancelled() && runner.os != 'Linux' }} + uses: actions/upload-artifact@v3 + with: + name: ${{ matrix.os }} Videos + path: ./tests/videos/** + + + test-and-upload-Linux: + # I couldn't figure out how to get these visual tests to actually run in the + # Debian Docker environment. If anyone can make it work, feel free to + # re-combine visual testing on Linux back into the main build job above! + # - DeeDeeG + runs-on: ubuntu-latest + needs: build + + steps: + - name: Checkout the latest code + uses: actions/checkout@v3 + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Restore Cached Pulsar Binaries - Linux + if: ${{ runner.os == 'Linux' }} + uses: actions/cache/restore@v3 + with: + path: ./binaries + key: pulsar-Linux-Binaries-${{ github.sha }}-${{ github.workflow }} + + - name: Restore Cached Pulsar dependencies - Linux + if: ${{ runner.os == 'Linux' }} + uses: actions/cache/restore@v3 + with: + path: | + node_modules + packages + key: Linux-dependencies-${{ github.sha }}-${{ github.workflow }} + + - name: Test Binary - Linux + if: ${{ (runner.os == 'Linux') && env.RUN_LINUX_VT }} + run: | + rm -R node_modules/electron; yarn install --check-files + ./binaries/*AppImage --appimage-extract + export BINARY_NAME='squashfs-root/pulsar' + mkdir -p ./tests/videos + Xvfb -screen 0 1024x768x24+32 :99 & nohup ffmpeg -video_size 1024x768 -f x11grab -i :99.0 ./tests/videos/out.mpg & DISPLAY=:99 PLAYWRIGHT_JUNIT_OUTPUT_NAME=report.xml npx playwright test --reporter=junit,list + + - name: Add binaries to Rolling Release Repo - Linux + if: ${{ github.event_name == 'push' && runner.os == 'Linux' }} + # We only want to upload rolling binaries if they are a commit to master + # Otherwise we want to not upload if it's a PR or manually triggered build + run: | + cd ./script/rolling-release-scripts + npm install + node ./rolling-release-binary-upload.js + + - name: Upload Video Artifacts - Linux + # Run whether this job passed or failed, unless explicitly cancelled. + if: ${{ !cancelled() && runner.os == 'Linux' }} uses: actions/upload-artifact@v3 with: name: ${{ matrix.os }} Videos diff --git a/LICENSE.md b/LICENSE.md index 50a586694..5c07eeda9 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022-2023 Pulsar-Edit +Copyright (c) 2022-2024 Pulsar-Edit Original work copyright (c) 2011-2022 GitHub Inc. Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/README.md b/README.md index 149f4f8b9..82f1a1258 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,9 @@ [![Badge Discussions]][Discussions]   [![Badge Discord]][Discord]   -[![Badge Reddit]][Reddit] +[![Badge Reddit]][Reddit]   +[![Badge Mastodon]][Mastodon]   +[![Badge Lemmy]][Lemmy]   [![Badge Status]][Status]   [![Badge License]][License] @@ -51,6 +53,8 @@ [Status]: https://cirrus-ci.com/github/pulsar-edit/pulsar/master [Codacy]: https://app.codacy.com/gh/pulsar-edit/pulsar [Reddit]: https://www.reddit.com/r/pulsaredit/ +[Mastodon]: https://fosstodon.org/@pulsaredit/ +[Lemmy]: https://lemmy.ml/c/pulsaredit/ [#]: # @@ -72,14 +76,18 @@ [Badge OpenCollective]: https://opencollective.com/pulsar-edit/tiers/badge.svg -[Badge Discussions]: https://img.shields.io/github/discussions/pulsar-edit/.github?style=for-the-badge&labelColor=78af9f&color=5a8377 +[Badge Discussions]: https://img.shields.io/github/discussions/pulsar-edit/.github?style=for-the-badge&logo=GitHub&labelColor=50555b&color=35393d [Badge Upstream]: https://img.shields.io/badge/Upstream_Status-Sunset-966227.svg?style=for-the-badge&labelColor=c38033 [Badge Discord]: https://img.shields.io/badge/Discord-4b7494.svg?style=for-the-badge&labelColor=6399c4&logoColor=white&logo=Discord [Badge License]: https://img.shields.io/badge/License-MIT-ac7f31.svg?style=for-the-badge&labelColor=e5ab42 [Badge Crowdin]: https://badges.crowdin.net/pulsar-edit/localized.svg [Badge Codacy]: https://app.codacy.com/project/badge/Grade/24873ecb93dc4c1d865202ce5b24efc1 -[Badge Reddit]: https://img.shields.io/reddit/subreddit-subscribers/pulsaredit?style=for-the-badge&label=Reddit&logoColor=white&logo=Reddit&labelColor=e05d44&color=b14835 + + +[Badge Reddit]: https://img.shields.io/badge/%2Fr%2Fpulsaredit-e05d44?style=for-the-badge&logo=Reddit&logoColor=white&labelColor=e05d44&color=b14835 [Badge Status]: https://img.shields.io/cirrus/github/pulsar-edit/pulsar?style=for-the-badge&labelColor=c77b7f&label=Build%20Status&color=8d575a +[Badge Mastodon]: https://img.shields.io/mastodon/follow/109416671848539153?domain=https%3A%2F%2Ffosstodon.org%2F&style=for-the-badge&logo=Mastodon&logoColor=white&label=Mastodon&labelColor=a0a0d8&color=7c7cc1 +[Badge Lemmy]: https://img.shields.io/lemmy/pulsaredit%40lemmy.ml?style=for-the-badge&logo=Lemmy&logoColor=white&label=Lemmy&labelColor=64ad82&color=1d9b52 diff --git a/spec/scope-resolver-spec.js b/spec/scope-resolver-spec.js index 51bced5b7..0aee61c60 100644 --- a/spec/scope-resolver-spec.js +++ b/spec/scope-resolver-spec.js @@ -132,6 +132,38 @@ describe('ScopeResolver', () => { } }); + it('provides the grammar with the text of leaf nodes only', async () => { + await grammar.setQueryForTest('highlightsQuery', ` + (expression_statement) @not_leaf_node + (call_expression) @also_not_leaf_node + (identifier) @leaf_node + (property_identifier) @also_leaf_node + `); + + let tokens = []; + const original = grammar.idForScope.bind(grammar); + grammar.idForScope = function(scope, text) { + if (text) { + tokens.push(text); + } + return original(scope, text); + }; + + const languageMode = new WASMTreeSitterLanguageMode({ grammar, buffer }); + buffer.setLanguageMode(languageMode); + buffer.setText('aa.bb(cc.dd());'); + await languageMode.ready; + + // If non-leaf nodes are included, this list would included things like + // 'aa.bb()' and `cc.dd()` + expect(tokens).toEqual([ + 'aa', + 'bb', + 'cc', + 'dd', + ]); + }); + it('interpolates magic tokens in scope names', async () => { await grammar.setQueryForTest('highlightsQuery', ` (lexical_declaration kind: _ @declaration._TYPE_) diff --git a/src/scope-resolver.js b/src/scope-resolver.js index 049b08e0d..1760e4a9d 100644 --- a/src/scope-resolver.js +++ b/src/scope-resolver.js @@ -499,7 +499,10 @@ class ScopeResolver { return false; } - let id = this.idForScope(name); + let id = this.idForScope( + name, + node.childCount === 0 ? node.text : undefined, + ); let { startPosition: start, diff --git a/src/tree-sitter-grammar.js b/src/tree-sitter-grammar.js index e76b8aed4..794dc79b6 100644 --- a/src/tree-sitter-grammar.js +++ b/src/tree-sitter-grammar.js @@ -70,7 +70,9 @@ module.exports = class TreeSitterGrammar { return `TreeSitterGrammar {scopeName: ${this.scopeName}}`; } - idForScope(scopeName) { + // Though _text is unused here, some packages (eg semanticolor) use it to + // customize scopes on the fly. + idForScope(scopeName, _text) { if (!scopeName) { return undefined; } diff --git a/src/ui.js b/src/ui.js index 3ee3fc3c6..2a4662c44 100644 --- a/src/ui.js +++ b/src/ui.js @@ -292,8 +292,8 @@ function renderMarkdown(content, givenOpts = {}) { if (match) { // We define breakline as a custom Token Type let token = state.push("html_inline", "breakline", 0); - token.content = "
"; - state.pos += "
".length; + token.content = "
"; + state.pos += match[0].length; return true; } } diff --git a/src/wasm-tree-sitter-grammar.js b/src/wasm-tree-sitter-grammar.js index 35182f9a9..fc6c87647 100644 --- a/src/wasm-tree-sitter-grammar.js +++ b/src/wasm-tree-sitter-grammar.js @@ -55,7 +55,9 @@ module.exports = class WASMTreeSitterGrammar { } } - idForScope(scopeName) { + // Though _text is unused here, some packages (eg semanticolor) use it to + // customize scopes on the fly. + idForScope(scopeName, _text) { if (!scopeName) { return undefined; } let id = this.idsByScope[scopeName]; if (!id) { diff --git a/src/wasm-tree-sitter-language-mode.js b/src/wasm-tree-sitter-language-mode.js index 7aa863530..e7e54df13 100644 --- a/src/wasm-tree-sitter-language-mode.js +++ b/src/wasm-tree-sitter-language-mode.js @@ -492,8 +492,8 @@ class WASMTreeSitterLanguageMode { return this.grammar.scopeNameForScopeId(scopeId); } - idForScope(name) { - return this.grammar.idForScope(name); + idForScope(name, text) { + return this.grammar.idForScope(name, text); } // Behaves like `scopeDescriptorForPosition`, but returns a list of @@ -3016,7 +3016,7 @@ class LanguageLayer { this.tree = null; this.scopeResolver = new ScopeResolver( this, - (name) => this.languageMode.idForScope(name) + (name, text) => this.languageMode.idForScope(name, text) ); this.foldResolver = new FoldResolver(this.buffer, this);