html/templateで key=val を出力する際は template.HTMLAttr型を使う

すごい久しぶりに goコードを書いたらいろいろと忘れていたのでメモ

templateに任意の属性を出力するようなコードを書きたかったが,

package main

import (
        "html/template"
        "log"
        "os"
)

var htmlTemplate = `
<p {{.Attr}}>
`

type Data struct {
        Attr string
}

func main() {
        t, err := template.New("test").Parse(htmlTemplate)
        if err != nil {
                log.Fatal(err)
        }

        d := Data{
                Attr: `class="foo"`,
        }

        if err := t.Execute(os.Stdout, &d); err != nil {
                log.Fatal(err)
        }
}

string と宣言してしまうと下記のようになってしまう

<p ZgotmplZ>

どうするだと思って調べたところ template.HTMLAttr 型を使えばよかった

package main

import (
    "html/template"
    "log"
    "os"
)

var htmlTemplate = `
<p {{.Attr}}>
`

type Data struct {
    Attr template.HTMLAttr
}

func main() {
    t, err := template.New("test").Parse(htmlTemplate)
    if err != nil {
        log.Fatal(err)
    }

    d := Data{
        Attr: `class="foo"`,
    }

    if err := t.Execute(os.Stdout, &d); err != nil {
        log.Fatal(err)
    }
}

出力は以下のようになる

<p class="foo">

ドキュメントにあるように信頼できる入力に対してのみ使うべきで何でも使うべきではないので利用の際は注意が必要

use-packageで :vcキーワードが使えるようになった

github.com

開発版だけど, use-packageで :vc キーワードがサポートされ標準のパッケージマネージャだけでも gitなどで管理されているパッケージも管理できるようになった. この機能がなかったため, やむおえず ここに書いたような設定を追加していたが:vc キーワードサポートにより不要になった. 以下のように書けば ok. 詳しくは最新版を入れて info use-package を参照してください.

(use-package editutil
  :vc (:url "https://github.com/syohex/emacs-editutil.git" :rev :newest)
  :config
  (editutil-default-setup)
  (set-face-attribute 'editutil-clean-space nil
                      :foreground "purple")
  (set-face-attribute 'editutil-vc-branch nil
                      :foreground "color-202"
                      :weight 'extra-bold))

Ubuntu 23.04にアップデートしたら clangdが動かなくなった問題の対応

先日 Ubuntu 23.04にアップデートしたら clangdが動かなくなり, Emacsの eglotや VScodeC++の補完等が一切効かなくなってしまった. その修正方法のメモ.

問題

clang, clang++が標準ヘッダファイルを見つけることができずファイルを解析できなくなってしまっていた.

原因

clang, clang++の include search pathが GCC 13のものになっており, GCC13がインストールされていないことが原因であった. clang++ -v で確かめてみると, GCC13のヘッダファイルを見ていることが確認できる.

% clang++ -v test.cpp

#include <...> search starts here:
 /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13
 /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/x86_64-linux-gnu/c++/13
 /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/backward
 /usr/lib/llvm-15/lib/clang/15.0.7/include
 /usr/local/include
 /usr/include/x86_64-linux-gnu
 /usr/include

なので gcc-13をインストールすることで問題は解消した

# gcc-13も入るので, g++を入れておく
sudo apt install g++-13

しかし Ubuntu 23.04の標準の gccは 12だし, gcc 13はそもそもインストールしていなかったのにその場所を見るというのも変である.

自分の環境による問題かは不明だが, これで問題は解消し, eglotも VSCodeも問題はなくなった.

el-getから package.el + use-packageに移行した

Emacsをあまりヘビーに使っていないし、できれば標準のものだけで済ませたいと思ってパッケージマネージャを el-getから標準ツールに移行した。前から考えていたんだけど、MELPAなどに登録されていないパッケージをいくつか使っていてそれで el-get使っていたんだけど、Emacs29から use-packageが標準パッケージ化、さらに package-vc.elも加わり、使い勝手はさておき移行は可能になったので移行してみた。(el-getが開発中の Emacsで動かない問題がありそのパッチが全然マージされないなどの理由もある)

package-vcは以下のようなシグネチャurl に git repositoryなどを渡せばそれをパッケージとして使える

(package-vc-install url rev nil name)

use-packageと組み合わせて使う

以下のような helperを用意し

(cl-defun from-github (&key repo name rev)
  (unless (package-installed-p name)
    (let ((url (format "https://github.com/%s" repo)))
      (package-vc-install url rev nil name))))

:init でインストール済みかをチェックするようにしてそれっぽく使えるようにした

(use-package editutil
  :init (from-github :repo "syohex/emacs-editutil" :name 'editutil)
  :config
  (editutil-default-setup)
  (set-face-attribute 'editutil-clean-space nil
                      :foreground "purple")
  (set-face-attribute 'editutil-vc-branch nil
                      :foreground "color-202"
                      :weight 'extra-bold))

とりあえず標準のパッケージマネジャーだけで必要なものだけはインストールできるようにはなった. まあただインストールが失敗することがあったり、使い勝手が el-getに比べると微妙な部分があるように思える. でもまあそこまでヘビーには使っていないのでひとまずこれでしばらくやってみる

2023 パズル in F#

他の人の回答などを見ずに愚直に解いてみたが全部を算出しようとすると遅すぎた.

type Opcode =
    | Add
    | Sub
    | Mul
    | Div

type Operand =
    | Opcode of Opcode
    | Number of int

let calculateRpnExpression (exp: Operand list) : Option<int> =
    let rec calculateRpnExpression' exp stack =
        match exp with
        | [] -> Some(List.head stack)
        | Number (n) :: t -> calculateRpnExpression' t (n :: stack)
        | Opcode (op) :: t ->
            match stack with
            | n2 :: n1 :: rest ->
                match op with
                | Add -> calculateRpnExpression' t ((n1 + n2) :: rest)
                | Sub -> calculateRpnExpression' t ((n1 - n2) :: rest)
                | Mul -> calculateRpnExpression' t ((n1 * n2) :: rest)
                | Div ->
                    if n2 = 0 then
                        None
                    else
                        calculateRpnExpression' t ((n1 / n2) :: rest)
            | _ ->
                printfn $"stack=${stack} exp=${exp}"
                failwith "never reach here"

    calculateRpnExpression' exp []

let normalizeRpnExpression (exp: Operand list) : string =
    let rec normalizeRpnExpression' exp stack =
        match exp with
        | [] -> List.head stack
        | Number (n) :: t -> normalizeRpnExpression' t ((string n) :: stack)
        | Opcode (op) :: t ->
            match stack with
            | v2 :: v1 :: rest ->
                match op with
                | Add -> normalizeRpnExpression' t ((sprintf "(%s+%s)" v1 v2) :: rest)
                | Sub -> normalizeRpnExpression' t ((sprintf "(%s-%s)" v1 v2) :: rest)
                | Mul -> normalizeRpnExpression' t ((sprintf "(%s*%s)" v1 v2) :: rest)
                | Div -> normalizeRpnExpression' t ((sprintf "(%s/%s)" v1 v2) :: rest)
            | _ -> failwith "never reach here"

    normalizeRpnExpression' exp []

let createRpnExpression (nums: int list) (target: int) =
    let len = nums |> List.length
    let limit = len * 2 - 1

    let rec createRpnExpression' nums prevNum numCount opCount exp acc =
        if numCount + opCount >= limit then
            let exp' = List.rev exp

            match calculateRpnExpression exp' with
            | None -> acc
            | Some (v) ->
                if v = target then
                    printfn "%A" (normalizeRpnExpression exp')
                    exp' :: acc
                else
                    acc
        else
            let acc' =
                if numCount - opCount >= 2 then
                    let ops =
                        if prevNum = 8 then
                            [ Div ]
                        else
                            [ Add; Sub; Mul; Div ]

                    ops
                    |> List.fold
                        (fun acc' op ->
                            createRpnExpression' nums -1 numCount (opCount + 1) (Opcode(op) :: exp) acc')
                        acc
                else
                    acc

            if prevNum <> 8 && numCount < len then
                let n = List.head nums
                createRpnExpression' (List.tail nums) n (numCount + 1) opCount (Number(n) :: exp) acc'
            else
                acc'

    createRpnExpression' nums -1 0 0 [] []


let quiz2023 (nums: int list) (target: int) : string list =
    let rpnExpressions = createRpnExpression nums target
    rpnExpressions |> List.map normalizeRpnExpression

この本に単純な場合のものは載っているので全くわからない人は見ても良いかもしれません。

Ubuntu 22.10に handbrakeをflatpakで インストールする

大昔の DVD整理を今更やろうとして bandbrakeをインストールしようとしたが, apt版だと SEGVが出てしまい使えなかったので flatpakで最新版を入れる必要があった. flatpakを使ったことがなかったのでそのメモ

flatpakのインストール

sudo apt install flatpak

最新版の handbrakeを flatpakでインストール

flatpak install https://dl.flathub.org/repo/appstream/fr.handbrake.ghb.flatpakref 

handbrakeを起動する

flatpak run fr.handbrake.ghb

clangdを使う際のメモ

最近ささっと C/C++書くとき, Emacsを使っているのだが, LSP serverとして clangdを使う際のメモ

Emacsの設定

Emacs開発版を使っているので, eglotをそのまま使う(C/C++ではデフォルトではサーバとして clangdが使われる)

(add-hook 'c-mode-hook #'eglot-ensure)
(add-hook 'c++-mode-hook #'eglot-ensure)

1ファイルの場合

特に対応が不要. よしなし補完なりエラーを表示してくれる

ビルドファイルがあるような本格的なプロジェクトの場合

各種フラグをセットしていたり, include pathを指定しておく必要があるので compile_commands.json を生成しておく必要がある. compile_commands.json はファイルの親ディレクトリかそのいずれかのディレクトリにある build/ サブディレクトリ以下にある必要がある. なのでビルドディレクトリ名には注意が必要となる.

CMakeの場合

CMAKE_EXPORT_COMPILE_COMMANDS というフラグがあるのでこれを onにしてビルドファイルを生成する

mkdir build/
cd build/
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=YES ..

Makefileの場合

compiledbbearMakefileから compile_commands.json を生成することができるので利用する. 一応ビルドせずファイルを生成する機能があるが, 1度ビルドした方が無難なようである.

# compiledbの場合
compiledb make

# bearの場合
bear -- make

これで補完やら定義元ジャンプやらが使えるようになる.